Browse Source

[ntp] Add simple NTP client

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 years ago
parent
commit
fce6117ad9
3 changed files with 385 additions and 0 deletions
  1. 1
    0
      src/include/ipxe/errfile.h
  2. 109
    0
      src/include/ipxe/ntp.h
  3. 275
    0
      src/net/udp/ntp.c

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -265,6 +265,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
265 265
 #define ERRFILE_peerblk			( ERRFILE_NET | 0x00460000 )
266 266
 #define ERRFILE_peermux			( ERRFILE_NET | 0x00470000 )
267 267
 #define ERRFILE_xsigo			( ERRFILE_NET | 0x00480000 )
268
+#define ERRFILE_ntp			( ERRFILE_NET | 0x00490000 )
268 269
 
269 270
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
270 271
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 109
- 0
src/include/ipxe/ntp.h View File

@@ -0,0 +1,109 @@
1
+#ifndef _IPXE_NTP_H
2
+#define _IPXE_NTP_H
3
+
4
+/** @file
5
+ *
6
+ * Network Time Protocol
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/in.h>
14
+#include <ipxe/interface.h>
15
+
16
+/** NTP port */
17
+#define NTP_PORT 123
18
+
19
+/** An NTP short-format timestamp */
20
+struct ntp_short {
21
+	/** Seconds */
22
+	uint16_t seconds;
23
+	/** Fraction of a second */
24
+	uint16_t fraction;
25
+} __attribute__ (( packed ));
26
+
27
+/** An NTP timestamp */
28
+struct ntp_timestamp {
29
+	/** Seconds */
30
+	uint32_t seconds;
31
+	/** Fraction of a second */
32
+	uint32_t fraction;
33
+} __attribute__ (( packed ));
34
+
35
+/** An NTP reference identifier */
36
+union ntp_id {
37
+	/** Textual identifier */
38
+	char text[4];
39
+	/** IPv4 address */
40
+	struct in_addr in;
41
+	/** Opaque integer */
42
+	uint32_t opaque;
43
+};
44
+
45
+/** An NTP header */
46
+struct ntp_header {
47
+	/** Flags */
48
+	uint8_t flags;
49
+	/** Stratum */
50
+	uint8_t stratum;
51
+	/** Polling rate */
52
+	int8_t poll;
53
+	/** Precision */
54
+	int8_t precision;
55
+	/** Root delay */
56
+	struct ntp_short delay;
57
+	/** Root dispersion */
58
+	struct ntp_short dispersion;
59
+	/** Reference clock identifier */
60
+	union ntp_id id;
61
+	/** Reference timestamp */
62
+	struct ntp_timestamp reference;
63
+	/** Originate timestamp */
64
+	struct ntp_timestamp originate;
65
+	/** Receive timestamp */
66
+	struct ntp_timestamp receive;
67
+	/** Transmit timestamp */
68
+	struct ntp_timestamp transmit;
69
+} __attribute__ (( packed ));
70
+
71
+/** Leap second indicator: unknown */
72
+#define NTP_FL_LI_UNKNOWN 0xc0
73
+
74
+/** NTP version: 1 */
75
+#define NTP_FL_VN_1 0x20
76
+
77
+/** NTP mode: client */
78
+#define NTP_FL_MODE_CLIENT 0x03
79
+
80
+/** NTP mode: server */
81
+#define NTP_FL_MODE_SERVER 0x04
82
+
83
+/** NTP mode mask */
84
+#define NTP_FL_MODE_MASK 0x07
85
+
86
+/** NTP timestamp for start of Unix epoch */
87
+#define NTP_EPOCH 2208988800UL
88
+
89
+/** NTP fraction of a second magic value
90
+ *
91
+ * This is a policy decision.
92
+ */
93
+#define NTP_FRACTION_MAGIC 0x69505845UL
94
+
95
+/** NTP minimum retransmission timeout
96
+ *
97
+ * This is a policy decision.
98
+ */
99
+#define NTP_MIN_TIMEOUT ( 1 * TICKS_PER_SEC )
100
+
101
+/** NTP maximum retransmission timeout
102
+ *
103
+ * This is a policy decision.
104
+ */
105
+#define NTP_MAX_TIMEOUT ( 10 * TICKS_PER_SEC )
106
+
107
+extern int start_ntp ( struct interface *job, const char *hostname );
108
+
109
+#endif /* _IPXE_NTP_H */

+ 275
- 0
src/net/udp/ntp.c View File

@@ -0,0 +1,275 @@
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/ntp.h>
40
+
41
+/** @file
42
+ *
43
+ * Network Time Protocol
44
+ *
45
+ */
46
+
47
+/** An NTP client */
48
+struct ntp_client {
49
+	/** Reference count */
50
+	struct refcnt refcnt;
51
+	/** Job control interface */
52
+	struct interface job;
53
+	/** Data transfer interface */
54
+	struct interface xfer;
55
+	/** Retransmission timer */
56
+	struct retry_timer timer;
57
+};
58
+
59
+/**
60
+ * Close NTP client
61
+ *
62
+ * @v ntp		NTP client
63
+ * @v rc		Reason for close
64
+ */
65
+static void ntp_close ( struct ntp_client *ntp, int rc ) {
66
+
67
+	/* Stop timer */
68
+	stop_timer ( &ntp->timer );
69
+
70
+	/* Shut down interfaces */
71
+	intf_shutdown ( &ntp->xfer, rc );
72
+	intf_shutdown ( &ntp->job, rc );
73
+}
74
+
75
+/**
76
+ * Send NTP request
77
+ *
78
+ * @v ntp		NTP client
79
+ * @ret rc		Return status code
80
+ */
81
+static int ntp_request ( struct ntp_client *ntp ) {
82
+	struct ntp_header hdr;
83
+	int rc;
84
+
85
+	DBGC ( ntp, "NTP %p sending request\n", ntp );
86
+
87
+	/* Construct header */
88
+	memset ( &hdr, 0, sizeof ( hdr ) );
89
+	hdr.flags = ( NTP_FL_LI_UNKNOWN | NTP_FL_VN_1 | NTP_FL_MODE_CLIENT );
90
+	hdr.transmit.seconds = htonl ( time ( NULL ) + NTP_EPOCH );
91
+	hdr.transmit.fraction = htonl ( NTP_FRACTION_MAGIC );
92
+
93
+	/* Send request */
94
+	if ( ( rc = xfer_deliver_raw ( &ntp->xfer, &hdr,
95
+				       sizeof ( hdr ) ) ) != 0 ) {
96
+		DBGC ( ntp, "NTP %p could not send request: %s\n",
97
+		       ntp, strerror ( rc ) );
98
+		return rc;
99
+	}
100
+
101
+	return 0;
102
+}
103
+
104
+/**
105
+ * Handle NTP response
106
+ *
107
+ * @v ntp		NTP client
108
+ * @v iobuf		I/O buffer
109
+ * @v meta		Data transfer metadata
110
+ * @ret rc		Return status code
111
+ */
112
+static int ntp_deliver ( struct ntp_client *ntp, struct io_buffer *iobuf,
113
+			 struct xfer_metadata *meta ) {
114
+	struct ntp_header *hdr;
115
+	struct sockaddr_tcpip *st_src;
116
+	int32_t delta;
117
+	int rc;
118
+
119
+	/* Check source port */
120
+	st_src = ( ( struct sockaddr_tcpip * ) meta->src );
121
+	if ( st_src->st_port != htons ( NTP_PORT ) ) {
122
+		DBGC ( ntp, "NTP %p received non-NTP packet:\n", ntp );
123
+		DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
124
+		goto ignore;
125
+	}
126
+
127
+	/* Check packet length */
128
+	if ( iob_len ( iobuf ) < sizeof ( *hdr ) ) {
129
+		DBGC ( ntp, "NTP %p received malformed packet:\n", ntp );
130
+		DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
131
+		goto ignore;
132
+	}
133
+	hdr = iobuf->data;
134
+
135
+	/* Check mode */
136
+	if ( ( hdr->flags & NTP_FL_MODE_MASK ) != NTP_FL_MODE_SERVER ) {
137
+		DBGC ( ntp, "NTP %p received non-server packet:\n", ntp );
138
+		DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
139
+		goto ignore;
140
+	}
141
+
142
+	/* Check magic value */
143
+	if ( hdr->originate.fraction != htonl ( NTP_FRACTION_MAGIC ) ) {
144
+		DBGC ( ntp, "NTP %p received unrecognised packet:\n", ntp );
145
+		DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
146
+		goto ignore;
147
+	}
148
+
149
+	/* Check for Kiss-o'-Death packets */
150
+	if ( ! hdr->stratum ) {
151
+		DBGC ( ntp, "NTP %p received kiss-o'-death:\n", ntp );
152
+		DBGC_HDA ( ntp, 0, iobuf->data, iob_len ( iobuf ) );
153
+		rc = -EPROTO;
154
+		goto close;
155
+	}
156
+
157
+	/* Calculate clock delta */
158
+	delta = ( ntohl ( hdr->receive.seconds ) -
159
+		  ntohl ( hdr->originate.seconds ) );
160
+	DBGC ( ntp, "NTP %p delta %d seconds\n", ntp, delta );
161
+
162
+	/* Adjust system clock */
163
+	time_adjust ( delta );
164
+
165
+	/* Success */
166
+	rc = 0;
167
+
168
+ close:
169
+	ntp_close ( ntp, rc );
170
+ ignore:
171
+	free_iob ( iobuf );
172
+	return 0;
173
+}
174
+
175
+/**
176
+ * Handle data transfer window change
177
+ *
178
+ * @v ntp		NTP client
179
+ */
180
+static void ntp_window_changed ( struct ntp_client *ntp ) {
181
+
182
+	/* Start timer to send initial request */
183
+	start_timer_nodelay ( &ntp->timer );
184
+}
185
+
186
+/** Data transfer interface operations */
187
+static struct interface_operation ntp_xfer_op[] = {
188
+	INTF_OP ( xfer_deliver, struct ntp_client *, ntp_deliver ),
189
+	INTF_OP ( xfer_window_changed, struct ntp_client *,
190
+		  ntp_window_changed ),
191
+	INTF_OP ( intf_close, struct ntp_client *, ntp_close ),
192
+};
193
+
194
+/** Data transfer interface descriptor */
195
+static struct interface_descriptor ntp_xfer_desc =
196
+	INTF_DESC_PASSTHRU ( struct ntp_client, xfer, ntp_xfer_op, job );
197
+
198
+/** Job control interface operations */
199
+static struct interface_operation ntp_job_op[] = {
200
+	INTF_OP ( intf_close, struct ntp_client *, ntp_close ),
201
+};
202
+
203
+/** Job control interface descriptor */
204
+static struct interface_descriptor ntp_job_desc =
205
+	INTF_DESC_PASSTHRU ( struct ntp_client, job, ntp_job_op, xfer );
206
+
207
+/**
208
+ * Handle NTP timer expiry
209
+ *
210
+ * @v timer		Retransmission timer
211
+ * @v fail		Failure indicator
212
+ */
213
+static void ntp_expired ( struct retry_timer *timer, int fail ) {
214
+	struct ntp_client *ntp =
215
+		container_of ( timer, struct ntp_client, timer );
216
+
217
+	/* Shut down client if we have failed */
218
+	if ( fail ) {
219
+		ntp_close ( ntp, -ETIMEDOUT );
220
+		return;
221
+	}
222
+
223
+	/* Otherwise, restart timer and (re)transmit request */
224
+	start_timer ( &ntp->timer );
225
+	ntp_request ( ntp );
226
+}
227
+
228
+/**
229
+ * Start NTP client
230
+ *
231
+ * @v job		Job control interface
232
+ * @v hostname		NTP server
233
+ * @ret rc		Return status code
234
+ */
235
+int start_ntp ( struct interface *job, const char *hostname ) {
236
+	struct ntp_client *ntp;
237
+	union {
238
+		struct sockaddr_tcpip st;
239
+		struct sockaddr sa;
240
+	} server;
241
+	int rc;
242
+
243
+	/* Allocate and initialise structure*/
244
+	ntp = zalloc ( sizeof ( *ntp ) );
245
+	if ( ! ntp ) {
246
+		rc = -ENOMEM;
247
+		goto err_alloc;
248
+	}
249
+	ref_init ( &ntp->refcnt, NULL );
250
+	intf_init ( &ntp->job, &ntp_job_desc, &ntp->refcnt );
251
+	intf_init ( &ntp->xfer, &ntp_xfer_desc, &ntp->refcnt );
252
+	timer_init ( &ntp->timer, ntp_expired, &ntp->refcnt );
253
+	set_timer_limits ( &ntp->timer, NTP_MIN_TIMEOUT, NTP_MAX_TIMEOUT );
254
+
255
+	/* Open socket */
256
+	memset ( &server, 0, sizeof ( server ) );
257
+	server.st.st_port = htons ( NTP_PORT );
258
+	if ( ( rc = xfer_open_named_socket ( &ntp->xfer, SOCK_DGRAM, &server.sa,
259
+					     hostname, NULL ) ) != 0 ) {
260
+		DBGC ( ntp, "NTP %p could not open socket: %s\n",
261
+		       ntp, strerror ( rc ) );
262
+		goto err_open;
263
+	}
264
+
265
+	/* Attach parent interface, mortalise self, and return */
266
+	intf_plug_plug ( &ntp->job, job );
267
+	ref_put ( &ntp->refcnt );
268
+	return 0;
269
+
270
+ err_open:
271
+	ntp_close ( ntp, rc );
272
+	ref_put ( &ntp->refcnt );
273
+ err_alloc:
274
+	return rc;
275
+}

Loading…
Cancel
Save