Преглед изворни кода

[ping] Add generic ping mechanism

Add generic pinger mechanism (analogous to the generic downloader
mechanism) which opens a ping socket, transmits ping requests, and
passes information about ping replies to a callback function.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown пре 11 година
родитељ
комит
c597c7a071
3 измењених фајлова са 327 додато и 0 уклоњено
  1. 303
    0
      src/core/pinger.c
  2. 1
    0
      src/include/ipxe/errfile.h
  3. 23
    0
      src/include/ipxe/pinger.h

+ 303
- 0
src/core/pinger.c Прегледај датотеку

@@ -0,0 +1,303 @@
1
+/*
2
+ * Copyright (C) 2013 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 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
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <stdlib.h>
23
+#include <string.h>
24
+#include <errno.h>
25
+#include <ipxe/refcnt.h>
26
+#include <ipxe/interface.h>
27
+#include <ipxe/job.h>
28
+#include <ipxe/xfer.h>
29
+#include <ipxe/iobuf.h>
30
+#include <ipxe/open.h>
31
+#include <ipxe/socket.h>
32
+#include <ipxe/retry.h>
33
+#include <ipxe/pinger.h>
34
+
35
+/** @file
36
+ *
37
+ * ICMP ping sender
38
+ *
39
+ */
40
+
41
+/* Disambiguate the various error causes */
42
+#define EPROTO_LEN __einfo_error ( EINFO_EPROTO_LEN )
43
+#define EINFO_EPROTO_LEN __einfo_uniqify ( EINFO_EPROTO, 0x01, \
44
+					   "Incorrect reply length" )
45
+#define EPROTO_DATA __einfo_error ( EINFO_EPROTO_DATA )
46
+#define EINFO_EPROTO_DATA __einfo_uniqify ( EINFO_EPROTO, 0x02, \
47
+					    "Incorrect reply data" )
48
+#define EPROTO_SEQ __einfo_error ( EINFO_EPROTO_SEQ )
49
+#define EINFO_EPROTO_SEQ __einfo_uniqify ( EINFO_EPROTO, 0x03, \
50
+					   "Delayed or out-of-sequence reply" )
51
+
52
+/** A pinger */
53
+struct pinger {
54
+	/** Reference count */
55
+	struct refcnt refcnt;
56
+
57
+	/** Job control interface */
58
+	struct interface job;
59
+	/** Data transfer interface */
60
+	struct interface xfer;
61
+
62
+	/** Timer */
63
+	struct retry_timer timer;
64
+	/** Timeout */
65
+	unsigned long timeout;
66
+
67
+	/** Payload length */
68
+	size_t len;
69
+	/** Current sequence number */
70
+	uint16_t sequence;
71
+
72
+	/** Callback function
73
+	 *
74
+	 * @v src		Source socket address
75
+	 * @v sequence		Sequence number
76
+	 * @v len		Payload length
77
+	 * @v rc		Status code
78
+	 */
79
+	void ( * callback ) ( struct sockaddr *src, unsigned int sequence,
80
+			      size_t len, int rc );
81
+};
82
+
83
+/**
84
+ * Generate payload
85
+ *
86
+ * @v pinger		Pinger
87
+ * @v data		Data buffer
88
+ */
89
+static void pinger_generate ( struct pinger *pinger, void *data ) {
90
+	uint8_t *bytes = data;
91
+	unsigned int i;
92
+
93
+	/* Generate byte sequence */
94
+	for ( i = 0 ; i < pinger->len ; i++ )
95
+		bytes[i] = ( i & 0xff );
96
+}
97
+
98
+/**
99
+ * Verify payload
100
+ *
101
+ * @v pinger		Pinger
102
+ * @v data		Data buffer
103
+ * @ret rc		Return status code
104
+ */
105
+static int pinger_verify ( struct pinger *pinger, const void *data ) {
106
+	const uint8_t *bytes = data;
107
+	unsigned int i;
108
+
109
+	/* Check byte sequence */
110
+	for ( i = 0 ; i < pinger->len ; i++ ) {
111
+		if ( bytes[i] != ( i & 0xff ) )
112
+			return -EPROTO_DATA;
113
+	}
114
+
115
+	return 0;
116
+}
117
+
118
+/**
119
+ * Close pinger
120
+ *
121
+ * @v pinger		Pinger
122
+ * @v rc		Reason for close
123
+ */
124
+static void pinger_close ( struct pinger *pinger, int rc ) {
125
+
126
+	/* Stop timer */
127
+	stop_timer ( &pinger->timer );
128
+
129
+	/* Shut down interfaces */
130
+	intf_shutdown ( &pinger->xfer, rc );
131
+	intf_shutdown ( &pinger->job, rc );
132
+}
133
+
134
+/**
135
+ * Handle data transfer window change
136
+ *
137
+ * @v pinger		Pinger
138
+ */
139
+static void pinger_window_changed ( struct pinger *pinger ) {
140
+
141
+	/* Do nothing if timer is already running */
142
+	if ( timer_running ( &pinger->timer ) )
143
+		return;
144
+
145
+	/* Start timer when window opens for the first time */
146
+	if ( xfer_window ( &pinger->xfer ) )
147
+		start_timer_nodelay ( &pinger->timer );
148
+}
149
+
150
+/**
151
+ * Handle timer expiry
152
+ *
153
+ * @v timer		Timer
154
+ * @v over		Failure indicator
155
+ */
156
+static void pinger_expired ( struct retry_timer *timer, int over __unused ) {
157
+	struct pinger *pinger = container_of ( timer, struct pinger, timer );
158
+	struct xfer_metadata meta;
159
+	struct io_buffer *iobuf;
160
+	int rc;
161
+
162
+	/* Increase sequence number */
163
+	pinger->sequence++;
164
+
165
+	/* Restart timer.  Do this before attempting to transmit, in
166
+	 * case the transmission attempt fails.
167
+	 */
168
+	start_timer_fixed ( &pinger->timer, pinger->timeout );
169
+
170
+	/* Allocate I/O buffer */
171
+	iobuf = xfer_alloc_iob ( &pinger->xfer, pinger->len );
172
+	if ( ! iobuf ) {
173
+		DBGC ( pinger, "PINGER %p could not allocate I/O buffer\n",
174
+		       pinger );
175
+		return;
176
+	}
177
+
178
+	/* Generate payload */
179
+	pinger_generate ( pinger, iob_put ( iobuf, pinger->len ) );
180
+
181
+	/* Generate metadata */
182
+	memset ( &meta, 0, sizeof ( meta ) );
183
+	meta.flags = XFER_FL_ABS_OFFSET;
184
+	meta.offset = pinger->sequence;
185
+
186
+	/* Transmit packet */
187
+	if ( ( rc = xfer_deliver ( &pinger->xfer, iobuf, &meta ) ) != 0 ) {
188
+		DBGC ( pinger, "PINGER %p could not transmit: %s\n",
189
+		       pinger, strerror ( rc ) );
190
+		return;
191
+	}
192
+}
193
+
194
+/**
195
+ * Handle received data
196
+ *
197
+ * @v pinger		Pinger
198
+ * @v iobuf		I/O buffer
199
+ * @v meta		Data transfer metadata
200
+ * @ret rc		Return status code
201
+ */
202
+static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf,
203
+			    struct xfer_metadata *meta ) {
204
+	size_t len = iob_len ( iobuf );
205
+	uint16_t sequence = meta->offset;
206
+	int rc;
207
+
208
+	/* Check for errors */
209
+	if ( len != pinger->len ) {
210
+		DBGC ( pinger, "PINGER %p received incorrect length %zd "
211
+		       "(expected %zd)\n", pinger, len, pinger->len );
212
+		rc = -EPROTO_LEN;
213
+	} else if ( ( rc = pinger_verify ( pinger, iobuf->data ) ) != 0 ) {
214
+		DBGC ( pinger, "PINGER %p received incorrect data:\n", pinger );
215
+		DBGC_HDA ( pinger, 0, iobuf->data, iob_len ( iobuf ) );
216
+	} else if ( sequence != pinger->sequence ) {
217
+		DBGC ( pinger, "PINGER %p received sequence %d (expected %d)\n",
218
+		       pinger, sequence, pinger->sequence );
219
+		rc = -EPROTO_SEQ;
220
+	} else {
221
+		rc = 0;
222
+	}
223
+
224
+	/* Discard I/O buffer */
225
+	free_iob ( iobuf );
226
+
227
+	/* Notify callback function */
228
+	pinger->callback ( meta->src, sequence, len, rc );
229
+
230
+	return rc;
231
+}
232
+
233
+/** Pinger data transfer interface operations */
234
+static struct interface_operation pinger_xfer_op[] = {
235
+	INTF_OP ( xfer_deliver, struct pinger *, pinger_deliver ),
236
+	INTF_OP ( xfer_window_changed, struct pinger *, pinger_window_changed ),
237
+	INTF_OP ( intf_close, struct pinger *, pinger_close ),
238
+};
239
+
240
+/** Pinger data transfer interface descriptor */
241
+static struct interface_descriptor pinger_xfer_desc =
242
+	INTF_DESC ( struct pinger, xfer, pinger_xfer_op );
243
+
244
+/** Pinger job control interface operations */
245
+static struct interface_operation pinger_job_op[] = {
246
+	INTF_OP ( intf_close, struct pinger *, pinger_close ),
247
+};
248
+
249
+/** Pinger job control interface descriptor */
250
+static struct interface_descriptor pinger_job_desc =
251
+	INTF_DESC ( struct pinger, job, pinger_job_op );
252
+
253
+/**
254
+ * Create pinger
255
+ *
256
+ * @v job		Job control interface
257
+ * @v hostname		Hostname to ping
258
+ * @v timeout		Timeout (in ticks)
259
+ * @v len		Payload length
260
+ * @ret rc		Return status code
261
+ */
262
+int create_pinger ( struct interface *job, const char *hostname,
263
+		    unsigned long timeout, size_t len,
264
+		    void ( * callback ) ( struct sockaddr *src,
265
+					  unsigned int sequence, size_t len,
266
+					  int rc ) ) {
267
+	struct pinger *pinger;
268
+	int rc;
269
+
270
+	/* Sanity check */
271
+	if ( ! timeout )
272
+		return -EINVAL;
273
+
274
+	/* Allocate and initialise structure */
275
+	pinger = zalloc ( sizeof ( *pinger ) );
276
+	if ( ! pinger )
277
+		return -ENOMEM;
278
+	ref_init ( &pinger->refcnt, NULL );
279
+	intf_init ( &pinger->job, &pinger_job_desc, &pinger->refcnt );
280
+	intf_init ( &pinger->xfer, &pinger_xfer_desc, &pinger->refcnt );
281
+	timer_init ( &pinger->timer, pinger_expired, &pinger->refcnt );
282
+	pinger->timeout = timeout;
283
+	pinger->len = len;
284
+	pinger->callback = callback;
285
+
286
+	/* Open socket */
287
+	if ( ( rc = xfer_open_named_socket ( &pinger->xfer, SOCK_ECHO, NULL,
288
+					     hostname, NULL ) ) != 0 ) {
289
+		DBGC ( pinger, "PINGER %p could not open socket: %s\n",
290
+		       pinger, strerror ( rc ) );
291
+		goto err;
292
+	}
293
+
294
+	/* Attach parent interface, mortalise self, and return */
295
+	intf_plug_plug ( &pinger->job, job );
296
+	ref_put ( &pinger->refcnt );
297
+	return 0;
298
+
299
+ err:
300
+	pinger_close ( pinger, rc );
301
+	ref_put ( &pinger->refcnt );
302
+	return rc;
303
+}

+ 1
- 0
src/include/ipxe/errfile.h Прегледај датотеку

@@ -64,6 +64,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
64 64
 #define ERRFILE_xferbuf		       ( ERRFILE_CORE | 0x00180000 )
65 65
 #define ERRFILE_pending		       ( ERRFILE_CORE | 0x00190000 )
66 66
 #define ERRFILE_null_reboot	       ( ERRFILE_CORE | 0x001a0000 )
67
+#define ERRFILE_pinger		       ( ERRFILE_CORE | 0x001b0000 )
67 68
 
68 69
 #define ERRFILE_eisa		     ( ERRFILE_DRIVER | 0x00000000 )
69 70
 #define ERRFILE_isa		     ( ERRFILE_DRIVER | 0x00010000 )

+ 23
- 0
src/include/ipxe/pinger.h Прегледај датотеку

@@ -0,0 +1,23 @@
1
+#ifndef _IPXE_PINGER_H
2
+#define _IPXE_PINGER_H
3
+
4
+/** @file
5
+ *
6
+ * ICMP ping sender
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/interface.h>
14
+#include <ipxe/socket.h>
15
+
16
+extern int create_pinger ( struct interface *job, const char *hostname,
17
+			   unsigned long timeout, size_t len,
18
+			   void ( * callback ) ( struct sockaddr *peer,
19
+						 unsigned int sequence,
20
+						 size_t len,
21
+						 int rc ) );
22
+
23
+#endif /* _IPXE_PINGER_H */

Loading…
Откажи
Сачувај