1 /*
2    This is a small demo of the NetX SNTP Client on the high-performance NetX TCP/IP stack.
3    This demo relies on Thread, NetX and NetX SNTP Client API to execute the Simple Network Time
4    Protocol in unicast and broadcast modes.
5 
6  */
7 
8 
9 #include <stdio.h>
10 #include "nx_api.h"
11 #include "nx_ip.h"
12 #include "nxd_sntp_client.h"
13 
14 /* Define SNTP packet size. */
15 #define NX_SNTP_CLIENT_PACKET_SIZE                 (NX_UDP_PACKET + 100)
16 
17 /* Define SNTP packet pool size. */
18 #define NX_SNTP_CLIENT_PACKET_POOL_SIZE            (4 * (NX_SNTP_CLIENT_PACKET_SIZE + sizeof(NX_PACKET)))
19 
20 /* Define how often the demo checks for SNTP updates. */
21 #define DEMO_PERIODIC_CHECK_INTERVAL                (1 * NX_IP_PERIODIC_RATE)
22 
23 /* Define how often we check on SNTP server status. We expect updates from the SNTP
24    server about every hour using the SNTP Client defaults. For testing make this (much) shorter. */
25 #define CHECK_SNTP_UPDATES_TIMEOUT                  (180 * NX_IP_PERIODIC_RATE)
26 
27 /* Set up generic network driver for demo program. */
28 void    _nx_ram_network_driver(struct NX_IP_DRIVER_STRUCT *driver_req);
29 
30 /* Application defined services of the NetX SNTP Client. */
31 
32 UINT leap_second_handler(NX_SNTP_CLIENT *client_ptr, UINT leap_indicator);
33 UINT kiss_of_death_handler(NX_SNTP_CLIENT *client_ptr, UINT KOD_code);
34 VOID time_update_callback(NX_SNTP_TIME_MESSAGE *time_update_ptr, NX_SNTP_TIME *local_time);
35 
36 
37 /* Set up client thread and network resources. */
38 
39 NX_PACKET_POOL      client_packet_pool;
40 NX_IP               client_ip;
41 TX_THREAD           demo_client_thread;
42 NX_SNTP_CLIENT      demo_sntp_client;
43 TX_EVENT_FLAGS_GROUP sntp_flags;
44 
45 #define DEMO_SNTP_UPDATE_EVENT  1
46 
47 /* Configure the SNTP Client to use IPv6. If not enabled, the
48    Client will use IPv4.  Note: IPv6 must be enabled in NetX Duo
49    for the Client to communicate over IPv6.    */
50 #ifdef FEATURE_NX_IPV6
51 /* #define USE_IPV6 */
52 #endif /* FEATURE_NX_IPV6 */
53 
54 
55 /* Configure the SNTP Client to use unicast SNTP. */
56 #define USE_UNICAST
57 
58 
59 #define CLIENT_IP_ADDRESS       IP_ADDRESS(192,2,2,66)
60 #define SERVER_IP_ADDRESS       IP_ADDRESS(192,2,2,92)
61 #define SERVER_IP_ADDRESS_2     SERVER_IP_ADDRESS
62 
63 /* Set up the SNTP network and address index; */
64 UINT     iface_index =0;
65 UINT     prefix = 64;
66 UINT     address_index;
67 
68 /* Set up client thread entry point. */
69 void    demo_client_thread_entry(ULONG info);
70 
71 /* Define main entry point.  */
main()72 int main()
73 {
74     /* Enter the ThreadX kernel.  */
75     tx_kernel_enter();
76     return 0;
77 }
78 
79 /* Define what the initial system looks like.  */
tx_application_define(void * first_unused_memory)80 void    tx_application_define(void *first_unused_memory)
81 {
82 
83 UINT     status;
84 UCHAR    *free_memory_pointer;
85 
86 
87     free_memory_pointer = (UCHAR *)first_unused_memory;
88 
89     /* Create client packet pool. */
90     status =  nx_packet_pool_create(&client_packet_pool, "SNTP Client Packet Pool",
91                                     NX_SNTP_CLIENT_PACKET_SIZE, free_memory_pointer,
92                                     NX_SNTP_CLIENT_PACKET_POOL_SIZE);
93 
94     /* Check for errors. */
95     if (status != NX_SUCCESS)
96     {
97 
98         return;
99     }
100 
101     /* Initialize the NetX system. */
102     nx_system_initialize();
103 
104     /* Update pointer to unallocated (free) memory. */
105     free_memory_pointer =  free_memory_pointer + NX_SNTP_CLIENT_PACKET_POOL_SIZE;
106 
107     /* Create Client IP instances */
108     status = nx_ip_create(&client_ip, "SNTP IP Instance", CLIENT_IP_ADDRESS,
109                           0xFFFFFF00UL, &client_packet_pool, _nx_ram_network_driver,
110                           free_memory_pointer, 2048, 1);
111 
112     /* Check for error. */
113     if (status != NX_SUCCESS)
114     {
115 
116         return;
117     }
118 
119     free_memory_pointer =  free_memory_pointer + 2048;
120 
121 #ifndef NX_DISABLE_IPV4
122     /* Enable ARP and supply ARP cache memory. */
123     status =  nx_arp_enable(&client_ip, (void **) free_memory_pointer, 2048);
124 
125     /* Check for error. */
126     if (status != NX_SUCCESS)
127     {
128 
129         return;
130     }
131 #endif /* NX_DISABLE_IPV4  */
132 
133     /* Update pointer to unallocated (free) memory. */
134     free_memory_pointer = free_memory_pointer + 2048;
135 
136     /* Enable UDP for client. */
137     status =  nx_udp_enable(&client_ip);
138 
139     /* Check for error. */
140     if (status != NX_SUCCESS)
141     {
142 
143         return;
144     }
145 
146 #ifndef NX_DISABLE_IPV4
147     status = nx_icmp_enable(&client_ip);
148 
149     /* Check for error. */
150     if (status != NX_SUCCESS)
151     {
152 
153         return;
154     }
155 #endif /* NX_DISABLE_IPV4  */
156 
157     /* Create the client thread */
158     status = tx_thread_create(&demo_client_thread, "SNTP Client Thread", demo_client_thread_entry,
159                               (ULONG)(&demo_sntp_client), free_memory_pointer, 2048,
160                               4, 4, TX_NO_TIME_SLICE, TX_DONT_START);
161 
162     /* Check for errors */
163     if (status != TX_SUCCESS)
164     {
165 
166         return;
167     }
168 
169     /* Create the event flags. */
170     status = tx_event_flags_create(&sntp_flags, "SNTP event flags");
171 
172     /* Check for errors */
173     if (status != TX_SUCCESS)
174     {
175 
176         return;
177     }
178 
179     /* Update pointer to unallocated (free) memory. */
180     free_memory_pointer = free_memory_pointer + 2048;
181 
182     /* set the SNTP network interface to the primary interface. */
183     iface_index = 0;
184 
185     /* Create the SNTP Client to run in broadcast mode.. */
186     status =  nx_sntp_client_create(&demo_sntp_client, &client_ip, iface_index, &client_packet_pool,
187                                     leap_second_handler,
188                                     kiss_of_death_handler,
189                                     NULL /* no random_number_generator callback */);
190 
191     /* Check for error. */
192     if (status != NX_SUCCESS)
193     {
194 
195         /* Bail out!*/
196         return;
197     }
198 
199     tx_thread_resume(&demo_client_thread);
200 
201     return;
202 }
203 
204 /* Define size of buffer to display client's local time. */
205 #define BUFSIZE 50
206 
207 /* Define the client thread.  */
demo_client_thread_entry(ULONG info)208 void    demo_client_thread_entry(ULONG info)
209 {
210 
211 UINT   status;
212 UINT   spin;
213 UINT   server_status;
214 ULONG  base_seconds;
215 ULONG  base_fraction;
216 ULONG  seconds, milliseconds, microseconds, fraction;
217 UINT   wait = 0;
218 UINT   error_counter = 0;
219 ULONG  events = 0;
220 #ifdef USE_IPV6
221 NXD_ADDRESS sntp_server_address;
222 NXD_ADDRESS client_ip_address;
223 #endif
224 
225     NX_PARAMETER_NOT_USED(info);
226 
227     /* Give other threads (IP instance) initialize first. */
228     tx_thread_sleep(NX_IP_PERIODIC_RATE);
229 
230 #ifdef USE_IPV6
231     /* Set up IPv6 services. */
232     status = nxd_ipv6_enable(&client_ip);
233 
234     status += nxd_icmp_enable(&client_ip);
235 
236     if (status  != NX_SUCCESS)
237         return;
238 
239     client_ip_address.nxd_ip_address.v6[0] = 0x20010db8;
240     client_ip_address.nxd_ip_address.v6[1] = 0x0000f101;
241     client_ip_address.nxd_ip_address.v6[2] = 0x0;
242     client_ip_address.nxd_ip_address.v6[3] = 0x101;
243     client_ip_address.nxd_ip_version = NX_IP_VERSION_V6;
244 
245     /* Set the IPv6 server address. */
246     sntp_server_address.nxd_ip_address.v6[0] = 0x20010db8;
247     sntp_server_address.nxd_ip_address.v6[1] = 0x0000f101;
248     sntp_server_address.nxd_ip_address.v6[2] = 0x0;
249     sntp_server_address.nxd_ip_address.v6[3] = 0x00000106;
250     sntp_server_address.nxd_ip_version = NX_IP_VERSION_V6;
251 
252     /* Establish the link local address for the host. The RAM driver creates
253        a virtual MAC address. */
254     status = nxd_ipv6_address_set(&client_ip, iface_index, NX_NULL, 10, NULL);
255 
256     /* Check for link local address set error.  */
257     if (status != NX_SUCCESS)
258     {
259         return;
260     }
261 
262      /* Set the host global IP address. We are assuming a 64
263        bit prefix here but this can be any value (< 128). */
264     status = nxd_ipv6_address_set(&client_ip, iface_index, &client_ip_address, prefix, &address_index);
265 
266     /* Check for global address set error.  */
267     if (status != NX_SUCCESS)
268     {
269         return;
270     }
271 
272     /* Wait while NetX Duo validates the global and link local addresses. */
273     tx_thread_sleep(5 * NX_IP_PERIODIC_RATE);
274 
275 #endif
276 
277     /* Setup time update callback function. */
278     nx_sntp_client_set_time_update_notify(&demo_sntp_client, time_update_callback);
279 
280     /* Set up client time updates depending on mode. */
281 #ifdef USE_UNICAST
282 
283     /* Initialize the Client for unicast mode to poll the SNTP server once an hour. */
284 #ifdef USE_IPV6
285     /* Use the duo service to set up the Client and set the IPv6 SNTP server. Note: this
286        can take either an IPv4 or IPv6 address. */
287     status = nxd_sntp_client_initialize_unicast(&demo_sntp_client, &sntp_server_address);
288 #else
289     /* Use the IPv4 service to set up the Client and set the IPv4 SNTP server. */
290     status = nx_sntp_client_initialize_unicast(&demo_sntp_client, SERVER_IP_ADDRESS);
291 #endif  /* USE_IPV6 */
292 
293 
294 #else   /* Broadcast mode */
295 
296     /* Initialize the Client for broadcast mode, no roundtrip calculation required and a broadcast SNTP service. */
297 #ifdef USE_IPV6
298     /* Use the duo service to initialize the Client and set IPv6 SNTP all hosts multicast address.
299        (Note: This can take either an IPv4 or IPv6 address.)*/
300     status = nxd_sntp_client_initialize_broadcast(&demo_sntp_client, &sntp_server_address, NX_NULL);
301 #else
302 
303     /* Use the IPv4 service to initialize the Client and set IPv4 SNTP broadcast address. */
304     status = nx_sntp_client_initialize_broadcast(&demo_sntp_client,  NX_NULL, SERVER_IP_ADDRESS);
305 #endif  /* USE_IPV6 */
306 #endif  /* USE_UNICAST */
307 
308     /* Check for error. */
309     if (status != NX_SUCCESS)
310     {
311         return;
312     }
313 
314     /* Set the base time which is approximately the number of seconds since the turn of the last century.
315        If this is not available in SNTP format, the nx_sntp_client_utility_add_msecs_to_ntp_time service
316        can convert milliseconds to fraction.  For how to compute NTP seconds from real time, read the
317        NetX SNTP User Guide.
318 
319        Otherwise set the base time to zero and set NX_SNTP_CLIENT_IGNORE_MAX_ADJUST_STARTUP to NX_TRUE for
320        the SNTP CLient to accept the first time update without applying a minimum or maximum adjustment
321        parameters (NX_SNTP_CLIENT_MIN_TIME_ADJUSTMENT and NX_SNTP_CLIENT_MAX_TIME_ADJUSTMENT). */
322 
323     base_seconds =  0xd2c96b90;  /* Jan 24, 2012 UTC */
324     base_fraction = 0xa132db1e;
325 
326     /* Apply to the SNTP Client local time.  */
327     status = nx_sntp_client_set_local_time(&demo_sntp_client, base_seconds, base_fraction);
328 
329     /* Check for error. */
330     if (status != NX_SUCCESS)
331     {
332         return;
333     }
334 
335     /* Run whichever service the client is configured for. */
336 #ifdef USE_UNICAST
337     status = nx_sntp_client_run_unicast(&demo_sntp_client);
338 #else
339     status = nx_sntp_client_run_broadcast(&demo_sntp_client);
340 #endif  /* USE_UNICAST */
341 
342     if (status != NX_SUCCESS)
343     {
344         return;
345     }
346 
347     spin = NX_TRUE;
348 
349     /* Now check periodically for time changes. */
350     while(spin)
351     {
352         /* Wait for a server update event. */
353         tx_event_flags_get(&sntp_flags, DEMO_SNTP_UPDATE_EVENT, TX_OR_CLEAR, &events, DEMO_PERIODIC_CHECK_INTERVAL);
354 
355         if (events == DEMO_SNTP_UPDATE_EVENT)
356         {
357 
358             /* Check for valid SNTP server status. */
359             status = nx_sntp_client_receiving_updates(&demo_sntp_client, &server_status);
360 
361             if ((status != NX_SUCCESS) || (server_status == NX_FALSE))
362             {
363 
364                 /* We do not have a valid update. Skip processing any time data. */
365 
366                 /* If this happens repeatedly, consider stopping the SNTP Client thread, picking another
367                    SNTP server and resuming the SNTP Client thread task (more details about that in the
368                    comments at the end of this function).
369 
370                    If SNTP Client configurable parameters are too restrictive, such as Max Adjustment, that may also cause
371                    valid server updates to be rejected. Configurable parameters, however, cannot be changed at run time.*/
372 
373                 continue;
374             }
375 
376             /* We have a valid update.  Get the SNTP Client time.  */
377             status = nx_sntp_client_get_local_time_extended(&demo_sntp_client, &seconds, &fraction, NX_NULL, 0);
378 
379             /* Convert fraction to microseconds. */
380             nx_sntp_client_utility_fraction_to_usecs(fraction, &microseconds);
381 
382             milliseconds = ((microseconds + 500) / 1000);
383 
384             if (status != NX_SUCCESS)
385             {
386                 printf("Internal error with getting local time 0x%x\n", status);
387                 error_counter++;
388             }
389             else
390             {
391                 printf("\nSNTP updated\n");
392                 printf("Time: %lu.%03lu sec.\r\n", seconds, milliseconds);
393             }
394 
395             /* Clear all events in our event flag. */
396             events = 0;
397         }
398         else
399         {
400 
401             /* No SNTP update event.
402 
403                In the meantime, if we have an RTC we might want to check its notion of time.
404                In this demo, we simulate the passage of time on our 'RTC' really just the CPU counter,
405                assuming that seconds and milliseconds have previously been set to a base (starting) time
406                (as was the SNTP Client before running it) */
407 
408             seconds += 1;      /* This is the sleep time (1 second) so is pretty close to an RTC */
409             milliseconds += 1; /* We don't know this value but for demonstration purposes we change it */
410 
411             /* Update our timer. */
412             wait += DEMO_PERIODIC_CHECK_INTERVAL;
413 
414             /* Check if it is time to display the local 'RTC' time. */
415             if (wait >= CHECK_SNTP_UPDATES_TIMEOUT)
416             {
417                 /* It is. Reset the timeout and print local time. */
418                 wait = 0;
419 
420                 printf("Time: %lu.%03lu sec.\r\n", seconds, milliseconds);
421             }
422         }
423     }
424 
425     /* We can stop the SNTP service if for example we think the SNTP server has stopped sending updates.
426 
427        To restart the SNTP Client, simply call the nx_sntp_client_initialize_unicast or nx_sntp_client_initialize_broadcast
428        using another SNTP server IP address as input, and resume the SNTP Client by calling nx_sntp_client_run_unicast or
429        nx_sntp_client_run_braodcast. */
430     status = nx_sntp_client_stop(&demo_sntp_client);
431 
432     if (status != NX_SUCCESS)
433     {
434         error_counter++;
435     }
436 
437     /* When done with the SNTP Client, we delete it */
438     status = nx_sntp_client_delete(&demo_sntp_client);
439 
440     return;
441 }
442 
443 
444 /* This application defined handler for handling an impending leap second is not
445    required by the SNTP Client. The default handler below only logs the event for
446    every time stamp received with the leap indicator set.  */
447 
leap_second_handler(NX_SNTP_CLIENT * client_ptr,UINT leap_indicator)448 UINT leap_second_handler(NX_SNTP_CLIENT *client_ptr, UINT leap_indicator)
449 {
450     NX_PARAMETER_NOT_USED(client_ptr);
451     NX_PARAMETER_NOT_USED(leap_indicator);
452 
453     /* Handle the leap second handler... */
454 
455     return NX_SUCCESS;
456 }
457 
458 /* This application defined handler for handling a Kiss of Death packet is not
459    required by the SNTP Client. A KOD handler should determine
460    if the Client task should continue vs. abort sending/receiving time data
461    from its current time server, and if aborting if it should remove
462    the server from its active server list.
463 
464    Note that the KOD list of codes is subject to change. The list
465    below is current at the time of this software release. */
466 
kiss_of_death_handler(NX_SNTP_CLIENT * client_ptr,UINT KOD_code)467 UINT kiss_of_death_handler(NX_SNTP_CLIENT *client_ptr, UINT KOD_code)
468 {
469 
470 UINT    remove_server_from_list = NX_FALSE;
471 UINT    status = NX_SUCCESS;
472 
473     NX_PARAMETER_NOT_USED(client_ptr);
474 
475     /* Handle kiss of death by code group. */
476     switch (KOD_code)
477     {
478 
479         case NX_SNTP_KOD_RATE:
480         case NX_SNTP_KOD_NOT_INIT:
481         case NX_SNTP_KOD_STEP:
482 
483             /* Find another server while this one is temporarily out of service.  */
484             status =  NX_SNTP_KOD_SERVER_NOT_AVAILABLE;
485 
486         break;
487 
488         case NX_SNTP_KOD_AUTH_FAIL:
489         case NX_SNTP_KOD_NO_KEY:
490         case NX_SNTP_KOD_CRYP_FAIL:
491 
492             /* These indicate the server will not service client with time updates
493                without successful authentication. */
494 
495 
496             remove_server_from_list =  NX_TRUE;
497 
498         break;
499 
500 
501         default:
502 
503             /* All other codes. Remove server before resuming time updates. */
504 
505             remove_server_from_list =  NX_TRUE;
506         break;
507     }
508 
509     /* Removing the server from the active server list? */
510     if (remove_server_from_list)
511     {
512 
513         /* Let the caller know it has to bail on this server before resuming service. */
514         status = NX_SNTP_KOD_REMOVE_SERVER;
515     }
516 
517     return status;
518 }
519 
520 
521 /* This application defined handler for notifying SNTP time update event.  */
522 
time_update_callback(NX_SNTP_TIME_MESSAGE * time_update_ptr,NX_SNTP_TIME * local_time)523 VOID time_update_callback(NX_SNTP_TIME_MESSAGE *time_update_ptr, NX_SNTP_TIME *local_time)
524 {
525     NX_PARAMETER_NOT_USED(time_update_ptr);
526     NX_PARAMETER_NOT_USED(local_time);
527 
528     tx_event_flags_set(&sntp_flags, DEMO_SNTP_UPDATE_EVENT, TX_OR);
529 }
530 
531