iPXE
ntp.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdint.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <time.h>
30 #include <ipxe/malloc.h>
31 #include <ipxe/refcnt.h>
32 #include <ipxe/iobuf.h>
33 #include <ipxe/xfer.h>
34 #include <ipxe/open.h>
35 #include <ipxe/retry.h>
36 #include <ipxe/timer.h>
37 #include <ipxe/time.h>
38 #include <ipxe/tcpip.h>
39 #include <ipxe/dhcp.h>
40 #include <ipxe/settings.h>
41 #include <ipxe/ntp.h>
42 
43 /** @file
44  *
45  * Network Time Protocol
46  *
47  */
48 
49 /** An NTP client */
50 struct ntp_client {
51  /** Reference count */
52  struct refcnt refcnt;
53  /** Job control interface */
54  struct interface job;
55  /** Data transfer interface */
56  struct interface xfer;
57  /** Retransmission timer */
59 };
60 
61 /**
62  * Close NTP client
63  *
64  * @v ntp NTP client
65  * @v rc Reason for close
66  */
67 static void ntp_close ( struct ntp_client *ntp, int rc ) {
68 
69  /* Stop timer */
70  stop_timer ( &ntp->timer );
71 
72  /* Shut down interfaces */
73  intf_shutdown ( &ntp->xfer, rc );
74  intf_shutdown ( &ntp->job, rc );
75 }
76 
77 /**
78  * Send NTP request
79  *
80  * @v ntp NTP client
81  * @ret rc Return status code
82  */
83 static int ntp_request ( struct ntp_client *ntp ) {
84  struct ntp_header hdr;
85  int rc;
86 
87  DBGC ( ntp, "NTP %p sending request\n", ntp );
88 
89  /* Construct header */
90  memset ( &hdr, 0, sizeof ( hdr ) );
92  hdr.transmit.seconds = htonl ( time ( NULL ) + NTP_EPOCH );
93  hdr.transmit.fraction = htonl ( NTP_FRACTION_MAGIC );
94 
95  /* Send request */
96  if ( ( rc = xfer_deliver_raw ( &ntp->xfer, &hdr,
97  sizeof ( hdr ) ) ) != 0 ) {
98  DBGC ( ntp, "NTP %p could not send request: %s\n",
99  ntp, strerror ( rc ) );
100  return rc;
101  }
102 
103  return 0;
104 }
105 
106 /**
107  * Handle NTP response
108  *
109  * @v ntp NTP client
110  * @v iobuf I/O buffer
111  * @v meta Data transfer metadata
112  * @ret rc Return status code
113  */
114 static int ntp_deliver ( struct ntp_client *ntp, struct io_buffer *iobuf,
115  struct xfer_metadata *meta ) {
116  struct ntp_header *hdr;
117  struct sockaddr_tcpip *st_src;
118  int32_t delta;
119  int rc;
120 
121  /* Check source port */
122  st_src = ( ( struct sockaddr_tcpip * ) meta->src );
123  if ( st_src->st_port != htons ( NTP_PORT ) ) {
124  DBGC ( ntp, "NTP %p received non-NTP packet:\n", ntp );
125  DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
126  goto ignore;
127  }
128 
129  /* Check packet length */
130  if ( iob_len ( iobuf ) < sizeof ( *hdr ) ) {
131  DBGC ( ntp, "NTP %p received malformed packet:\n", ntp );
132  DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
133  goto ignore;
134  }
135  hdr = iobuf->data;
136 
137  /* Check mode */
138  if ( ( hdr->flags & NTP_FL_MODE_MASK ) != NTP_FL_MODE_SERVER ) {
139  DBGC ( ntp, "NTP %p received non-server packet:\n", ntp );
140  DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
141  goto ignore;
142  }
143 
144  /* Check magic value */
145  if ( hdr->originate.fraction != htonl ( NTP_FRACTION_MAGIC ) ) {
146  DBGC ( ntp, "NTP %p received unrecognised packet:\n", ntp );
147  DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
148  goto ignore;
149  }
150 
151  /* Check for Kiss-o'-Death packets */
152  if ( ! hdr->stratum ) {
153  DBGC ( ntp, "NTP %p received kiss-o'-death:\n", ntp );
154  DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
155  rc = -EPROTO;
156  goto close;
157  }
158 
159  /* Calculate clock delta */
160  delta = ( ntohl ( hdr->receive.seconds ) -
161  ntohl ( hdr->originate.seconds ) );
162  DBGC ( ntp, "NTP %p delta %d seconds\n", ntp, delta );
163 
164  /* Adjust system clock */
165  time_adjust ( delta );
166 
167  /* Success */
168  rc = 0;
169 
170  close:
171  ntp_close ( ntp, rc );
172  ignore:
173  free_iob ( iobuf );
174  return 0;
175 }
176 
177 /**
178  * Handle data transfer window change
179  *
180  * @v ntp NTP client
181  */
182 static void ntp_window_changed ( struct ntp_client *ntp ) {
183 
184  /* Start timer to send initial request */
185  start_timer_nodelay ( &ntp->timer );
186 }
187 
188 /** Data transfer interface operations */
189 static struct interface_operation ntp_xfer_op[] = {
193  INTF_OP ( intf_close, struct ntp_client *, ntp_close ),
194 };
195 
196 /** Data transfer interface descriptor */
198  INTF_DESC_PASSTHRU ( struct ntp_client, xfer, ntp_xfer_op, job );
199 
200 /** Job control interface operations */
201 static struct interface_operation ntp_job_op[] = {
202  INTF_OP ( intf_close, struct ntp_client *, ntp_close ),
203 };
204 
205 /** Job control interface descriptor */
207  INTF_DESC_PASSTHRU ( struct ntp_client, job, ntp_job_op, xfer );
208 
209 /**
210  * Handle NTP timer expiry
211  *
212  * @v timer Retransmission timer
213  * @v fail Failure indicator
214  */
215 static void ntp_expired ( struct retry_timer *timer, int fail ) {
216  struct ntp_client *ntp =
217  container_of ( timer, struct ntp_client, timer );
218 
219  /* Shut down client if we have failed */
220  if ( fail ) {
221  ntp_close ( ntp, -ETIMEDOUT );
222  return;
223  }
224 
225  /* Otherwise, restart timer and (re)transmit request */
226  start_timer ( &ntp->timer );
227  ntp_request ( ntp );
228 }
229 
230 /**
231  * Start NTP client
232  *
233  * @v job Job control interface
234  * @v hostname NTP server
235  * @ret rc Return status code
236  */
237 int start_ntp ( struct interface *job, const char *hostname ) {
238  struct ntp_client *ntp;
239  union {
240  struct sockaddr_tcpip st;
241  struct sockaddr sa;
242  } server;
243  int rc;
244 
245  /* Allocate and initialise structure*/
246  ntp = zalloc ( sizeof ( *ntp ) );
247  if ( ! ntp ) {
248  rc = -ENOMEM;
249  goto err_alloc;
250  }
251  ref_init ( &ntp->refcnt, NULL );
252  intf_init ( &ntp->job, &ntp_job_desc, &ntp->refcnt );
253  intf_init ( &ntp->xfer, &ntp_xfer_desc, &ntp->refcnt );
254  timer_init ( &ntp->timer, ntp_expired, &ntp->refcnt );
255  set_timer_limits ( &ntp->timer, NTP_MIN_TIMEOUT, NTP_MAX_TIMEOUT );
256 
257  /* Open socket */
258  memset ( &server, 0, sizeof ( server ) );
259  server.st.st_port = htons ( NTP_PORT );
260  if ( ( rc = xfer_open_named_socket ( &ntp->xfer, SOCK_DGRAM, &server.sa,
261  hostname, NULL ) ) != 0 ) {
262  DBGC ( ntp, "NTP %p could not open socket: %s\n",
263  ntp, strerror ( rc ) );
264  goto err_open;
265  }
266 
267  /* Attach parent interface, mortalise self, and return */
268  intf_plug_plug ( &ntp->job, job );
269  ref_put ( &ntp->refcnt );
270  return 0;
271 
272  err_open:
273  ntp_close ( ntp, rc );
274  ref_put ( &ntp->refcnt );
275  err_alloc:
276  return rc;
277 }
278 
279 /** IPv4 NTP server setting */
280 const struct setting ntp_setting __setting ( SETTING_IP4_EXTRA, ntp ) = {
281  .name = "ntp",
282  .description = "NTP server",
283  .tag = DHCP_NTP_SERVERS,
284  .type = &setting_type_ipv4,
285 };
An object interface operation.
Definition: interface.h:17
#define DHCP_NTP_SERVERS
NTP servers.
Definition: dhcp.h:90
TCP/IP socket address.
Definition: tcpip.h:75
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
void xfer_window_changed(struct interface *intf)
Report change of flow control window.
Definition: xfer.c:146
void intf_close(struct interface *intf, int rc)
Close an object interface.
Definition: interface.c:249
Dynamic Host Configuration Protocol.
Data transfer metadata.
Definition: xfer.h:22
void intf_shutdown(struct interface *intf, int rc)
Shut down an object interface.
Definition: interface.c:278
static void start_timer_nodelay(struct retry_timer *timer)
Start timer with no delay.
Definition: retry.h:99
static void ntp_window_changed(struct ntp_client *ntp)
Handle data transfer window change.
Definition: ntp.c:182
#define ref_init(refcnt, free)
Initialise a reference counter.
Definition: refcnt.h:64
Error codes.
struct golan_inbox_hdr hdr
Message header.
Definition: CIB_PRM.h:28
I/O buffers.
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition: iobuf.c:146
#define SOCK_DGRAM
Definition: socket.h:29
Retry timers.
#define DBGC(...)
Definition: compiler.h:505
A retry timer.
Definition: retry.h:21
void intf_plug_plug(struct interface *a, struct interface *b)
Plug two object interfaces together.
Definition: interface.c:107
#define ntohl(value)
Definition: byteswap.h:134
#define NTP_EPOCH
NTP timestamp for start of Unix epoch.
Definition: ntp.h:87
int start_ntp(struct interface *job, const char *hostname)
Start NTP client.
Definition: ntp.c:237
iPXE timers
An NTP header.
Definition: ntp.h:46
#define NTP_FL_VN_1
NTP version: 1.
Definition: ntp.h:75
Network Time Protocol.
#define htonl(value)
Definition: byteswap.h:133
#define NTP_FL_MODE_CLIENT
NTP mode: client.
Definition: ntp.h:78
int xfer_deliver_raw(struct interface *intf, const void *data, size_t len)
Deliver datagram as raw data without metadata.
Definition: xfer.c:288
Dynamic memory allocation.
Data transfer interfaces.
A reference counter.
Definition: refcnt.h:26
#define NTP_FRACTION_MAGIC
NTP fraction of a second magic value.
Definition: ntp.h:93
A timer.
Definition: timer.h:28
const char * name
Name.
Definition: settings.h:28
struct sockaddr_tcpip st
Definition: syslog.c:56
An NTP client.
Definition: ntp.c:50
#define ENOMEM
Not enough space.
Definition: errno.h:534
#define SETTING_IP4_EXTRA
IPv4 additional settings.
Definition: settings.h:66
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:35
An object interface.
Definition: interface.h:124
#define DBGC_HDA(...)
Definition: compiler.h:506
struct interface xfer
Data transfer interface.
Definition: ntp.c:56
struct sockaddr sa
Definition: syslog.c:55
Transport-network layer interface.
int ntp(const char *hostname)
Get time and date via NTP.
Definition: ntpmgmt.c:45
#define EPROTO
Protocol error.
Definition: errno.h:624
int meta(WINDOW *, bool)
Configuration settings.
uint16_t st_port
TCP/IP port.
Definition: tcpip.h:81
Generalized socket address structure.
Definition: socket.h:96
An object interface descriptor.
Definition: interface.h:55
#define NTP_PORT
NTP port.
Definition: ntp.h:17
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
const struct setting ntp_setting __setting(SETTING_IP4_EXTRA, ntp)
IPv4 NTP server setting.
void * zalloc(size_t size)
Allocate cleared memory.
Definition: malloc.c:624
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition: iobuf.h:155
#define INTF_OP(op_type, object_type, op_func)
Define an object interface operation.
Definition: interface.h:32
static struct interface_descriptor ntp_xfer_desc
Data transfer interface descriptor.
Definition: ntp.c:197
static struct interface_descriptor ntp_job_desc
Job control interface descriptor.
Definition: ntp.c:206
static int ntp_request(struct ntp_client *ntp)
Send NTP request.
Definition: ntp.c:83
int xfer_deliver(struct interface *intf, struct io_buffer *iobuf, struct xfer_metadata *meta)
Deliver datagram.
Definition: xfer.c:194
Data transfer interface opening.
#define NTP_FL_LI_UNKNOWN
Leap second indicator: unknown.
Definition: ntp.h:72
A setting.
Definition: settings.h:23
#define NTP_MIN_TIMEOUT
NTP minimum retransmission timeout.
Definition: ntp.h:99
void start_timer(struct retry_timer *timer)
Start timer.
Definition: retry.c:93
static int ntp_deliver(struct ntp_client *ntp, struct io_buffer *iobuf, struct xfer_metadata *meta)
Handle NTP response.
Definition: ntp.c:114
void stop_timer(struct retry_timer *timer)
Stop timer.
Definition: retry.c:117
static struct interface_operation ntp_xfer_op[]
Data transfer interface operations.
Definition: ntp.c:189
static void ntp_close(struct ntp_client *ntp, int rc)
Close NTP client.
Definition: ntp.c:67
signed int int32_t
Definition: stdint.h:17
#define NTP_MAX_TIMEOUT
NTP maximum retransmission timeout.
Definition: ntp.h:105
void * data
Start of data.
Definition: iobuf.h:48
Reference counting.
static void ntp_expired(struct retry_timer *timer, int fail)
Handle NTP timer expiry.
Definition: ntp.c:215
#define INTF_DESC_PASSTHRU(object_type, intf, operations, passthru)
Define an object interface descriptor with pass-through interface.
Definition: interface.h:97
#define NTP_FL_MODE_SERVER
NTP mode: server.
Definition: ntp.h:81
static struct evtchn_close * close
Definition: xenevent.h:23
#define NTP_FL_MODE_MASK
NTP mode mask.
Definition: ntp.h:84
static void intf_init(struct interface *intf, struct interface_descriptor *desc, struct refcnt *refcnt)
Initialise an object interface.
Definition: interface.h:203
Time source.
struct interface job
Job control interface.
Definition: ntp.c:54
uint64_t time
Current time.
Definition: ntlm.h:20
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
#define ETIMEDOUT
Connection timed out.
Definition: errno.h:669
String functions.
static struct interface_operation ntp_job_op[]
Job control interface operations.
Definition: ntp.c:201
#define htons(value)
Definition: byteswap.h:135
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
int xfer_open_named_socket(struct interface *xfer, int semantics, struct sockaddr *peer, const char *name, struct sockaddr *local)
Open named socket.
Definition: resolv.c:402
#define ref_put(refcnt)
Drop reference to object.
Definition: refcnt.h:106
void * memset(void *dest, int character, size_t len) __nonnull
A persistent I/O buffer.
Definition: iobuf.h:33