iPXE
pinger.c
Go to the documentation of this file.
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  * 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 FILE_SECBOOT ( PERMITTED );
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <ipxe/refcnt.h>
31 #include <ipxe/interface.h>
32 #include <ipxe/job.h>
33 #include <ipxe/xfer.h>
34 #include <ipxe/iobuf.h>
35 #include <ipxe/open.h>
36 #include <ipxe/socket.h>
37 #include <ipxe/retry.h>
38 #include <ipxe/pinger.h>
39 
40 /** @file
41  *
42  * ICMP ping sender
43  *
44  */
45 
46 /* Disambiguate the various error causes */
47 #define EPROTO_LEN __einfo_error ( EINFO_EPROTO_LEN )
48 #define EINFO_EPROTO_LEN __einfo_uniqify ( EINFO_EPROTO, 0x01, \
49  "Incorrect reply length" )
50 #define EPROTO_DATA __einfo_error ( EINFO_EPROTO_DATA )
51 #define EINFO_EPROTO_DATA __einfo_uniqify ( EINFO_EPROTO, 0x02, \
52  "Incorrect reply data" )
53 #define EPROTO_SEQ __einfo_error ( EINFO_EPROTO_SEQ )
54 #define EINFO_EPROTO_SEQ __einfo_uniqify ( EINFO_EPROTO, 0x03, \
55  "Delayed or out-of-sequence reply" )
56 
57 /** A pinger */
58 struct pinger {
59  /** Reference count */
60  struct refcnt refcnt;
61 
62  /** Job control interface */
63  struct interface job;
64  /** Data transfer interface */
65  struct interface xfer;
66 
67  /** Timer */
69  /** Timeout */
70  unsigned long timeout;
71 
72  /** Payload length */
73  size_t len;
74  /** Current sequence number */
76  /** Response for current sequence number is still pending */
77  int pending;
78  /** Number of remaining expiry events (zero to continue indefinitely) */
79  unsigned int remaining;
80  /** Return status */
81  int rc;
82 
83  /** Callback function
84  *
85  * @v src Source socket address, or NULL
86  * @v sequence Sequence number
87  * @v len Payload length
88  * @v rc Status code
89  */
90  void ( * callback ) ( struct sockaddr *src, unsigned int sequence,
91  size_t len, int rc );
92 };
93 
94 /**
95  * Generate payload
96  *
97  * @v pinger Pinger
98  * @v data Data buffer
99  */
100 static void pinger_generate ( struct pinger *pinger, void *data ) {
101  uint8_t *bytes = data;
102  unsigned int i;
103 
104  /* Generate byte sequence */
105  for ( i = 0 ; i < pinger->len ; i++ )
106  bytes[i] = ( i & 0xff );
107 }
108 
109 /**
110  * Verify payload
111  *
112  * @v pinger Pinger
113  * @v data Data buffer
114  * @ret rc Return status code
115  */
116 static int pinger_verify ( struct pinger *pinger, const void *data ) {
117  const uint8_t *bytes = data;
118  unsigned int i;
119 
120  /* Check byte sequence */
121  for ( i = 0 ; i < pinger->len ; i++ ) {
122  if ( bytes[i] != ( i & 0xff ) )
123  return -EPROTO_DATA;
124  }
125 
126  return 0;
127 }
128 
129 /**
130  * Close pinger
131  *
132  * @v pinger Pinger
133  * @v rc Reason for close
134  */
135 static void pinger_close ( struct pinger *pinger, int rc ) {
136 
137  /* Stop timer */
138  stop_timer ( &pinger->timer );
139 
140  /* Shut down interfaces */
141  intf_shutdown ( &pinger->xfer, rc );
142  intf_shutdown ( &pinger->job, rc );
143 }
144 
145 /**
146  * Handle data transfer window change
147  *
148  * @v pinger Pinger
149  */
150 static void pinger_window_changed ( struct pinger *pinger ) {
151 
152  /* Do nothing if timer is already running */
153  if ( timer_running ( &pinger->timer ) )
154  return;
155 
156  /* Start timer when window opens for the first time */
157  if ( xfer_window ( &pinger->xfer ) )
159 }
160 
161 /**
162  * Handle timer expiry
163  *
164  * @v timer Timer
165  * @v over Failure indicator
166  */
167 static void pinger_expired ( struct retry_timer *timer, int over __unused ) {
168  struct pinger *pinger = container_of ( timer, struct pinger, timer );
169  struct xfer_metadata meta;
170  struct io_buffer *iobuf;
171  int rc;
172 
173  /* If no response has been received, notify the callback function */
174  if ( pinger->pending && pinger->callback )
176 
177  /* Check for termination */
178  if ( pinger->remaining && ( --pinger->remaining == 0 ) ) {
180  return;
181  }
182 
183  /* Increase sequence number */
184  pinger->sequence++;
185 
186  /* Restart timer. Do this before attempting to transmit, in
187  * case the transmission attempt fails.
188  */
190  pinger->pending = 1;
191 
192  /* Allocate I/O buffer */
193  iobuf = xfer_alloc_iob ( &pinger->xfer, pinger->len );
194  if ( ! iobuf ) {
195  DBGC ( pinger, "PINGER %p could not allocate I/O buffer\n",
196  pinger );
197  return;
198  }
199 
200  /* Generate payload */
201  pinger_generate ( pinger, iob_put ( iobuf, pinger->len ) );
202 
203  /* Generate metadata */
204  memset ( &meta, 0, sizeof ( meta ) );
205  meta.flags = XFER_FL_ABS_OFFSET;
206  meta.offset = pinger->sequence;
207 
208  /* Transmit packet */
209  if ( ( rc = xfer_deliver ( &pinger->xfer, iobuf, &meta ) ) != 0 ) {
210  DBGC ( pinger, "PINGER %p could not transmit: %s\n",
211  pinger, strerror ( rc ) );
212  return;
213  }
214 }
215 
216 /**
217  * Handle received data
218  *
219  * @v pinger Pinger
220  * @v iobuf I/O buffer
221  * @v meta Data transfer metadata
222  * @ret rc Return status code
223  */
224 static int pinger_deliver ( struct pinger *pinger, struct io_buffer *iobuf,
225  struct xfer_metadata *meta ) {
226  size_t len = iob_len ( iobuf );
227  uint16_t sequence = meta->offset;
228  int terminate = 0;
229  int rc;
230 
231  /* Clear response pending flag, if applicable */
232  if ( sequence == pinger->sequence )
233  pinger->pending = 0;
234 
235  /* Check for errors */
236  if ( len != pinger->len ) {
237  /* Incorrect length: terminate immediately if we are
238  * not pinging indefinitely.
239  */
240  DBGC ( pinger, "PINGER %p received incorrect length %zd "
241  "(expected %zd)\n", pinger, len, pinger->len );
242  rc = -EPROTO_LEN;
243  terminate = ( pinger->remaining != 0 );
244  } else if ( ( rc = pinger_verify ( pinger, iobuf->data ) ) != 0 ) {
245  /* Incorrect data: terminate immediately if we are not
246  * pinging indefinitely.
247  */
248  DBGC ( pinger, "PINGER %p received incorrect data:\n", pinger );
249  DBGC_HDA ( pinger, 0, iobuf->data, iob_len ( iobuf ) );
250  terminate = ( pinger->remaining != 0 );
251  } else if ( sequence != pinger->sequence ) {
252  /* Incorrect sequence number (probably a delayed response):
253  * report via callback but otherwise ignore.
254  */
255  DBGC ( pinger, "PINGER %p received sequence %d (expected %d)\n",
256  pinger, sequence, pinger->sequence );
257  rc = -EPROTO_SEQ;
258  terminate = 0;
259  } else {
260  /* Success: record that a packet was successfully received,
261  * and terminate if we expect to send no further packets.
262  */
263  rc = 0;
264  pinger->rc = 0;
265  terminate = ( pinger->remaining == 1 );
266  }
267 
268  /* Discard I/O buffer */
269  free_iob ( iobuf );
270 
271  /* Notify callback function, if applicable */
272  if ( pinger->callback )
273  pinger->callback ( meta->src, sequence, len, rc );
274 
275  /* Terminate if applicable */
276  if ( terminate )
277  pinger_close ( pinger, rc );
278 
279  return rc;
280 }
281 
282 /** Pinger data transfer interface operations */
284  INTF_OP ( xfer_deliver, struct pinger *, pinger_deliver ),
286  INTF_OP ( intf_close, struct pinger *, pinger_close ),
287 };
288 
289 /** Pinger data transfer interface descriptor */
291  INTF_DESC ( struct pinger, xfer, pinger_xfer_op );
292 
293 /** Pinger job control interface operations */
295  INTF_OP ( intf_close, struct pinger *, pinger_close ),
296 };
297 
298 /** Pinger job control interface descriptor */
300  INTF_DESC ( struct pinger, job, pinger_job_op );
301 
302 /**
303  * Create pinger
304  *
305  * @v job Job control interface
306  * @v hostname Hostname to ping
307  * @v timeout Timeout (in ticks)
308  * @v len Payload length
309  * @v count Number of packets to send (or zero for no limit)
310  * @v callback Callback function (or NULL)
311  * @ret rc Return status code
312  */
313 int create_pinger ( struct interface *job, const char *hostname,
314  unsigned long timeout, size_t len, unsigned int count,
315  void ( * callback ) ( struct sockaddr *src,
316  unsigned int sequence, size_t len,
317  int rc ) ) {
318  struct pinger *pinger;
319  int rc;
320 
321  /* Sanity check */
322  if ( ! timeout )
323  return -EINVAL;
324 
325  /* Allocate and initialise structure */
326  pinger = zalloc ( sizeof ( *pinger ) );
327  if ( ! pinger )
328  return -ENOMEM;
329  ref_init ( &pinger->refcnt, NULL );
332  timer_init ( &pinger->timer, pinger_expired, &pinger->refcnt );
334  pinger->len = len;
335  pinger->remaining = ( count ? ( count + 1 /* Initial packet */ ) : 0 );
337  pinger->rc = -ETIMEDOUT;
338 
339  /* Open socket */
341  hostname, NULL ) ) != 0 ) {
342  DBGC ( pinger, "PINGER %p could not open socket: %s\n",
343  pinger, strerror ( rc ) );
344  goto err;
345  }
346 
347  /* Attach parent interface, mortalise self, and return */
348  intf_plug_plug ( &pinger->job, job );
349  ref_put ( &pinger->refcnt );
350  return 0;
351 
352  err:
353  pinger_close ( pinger, rc );
354  ref_put ( &pinger->refcnt );
355  return rc;
356 }
#define EINVAL
Invalid argument.
Definition: errno.h:429
An object interface operation.
Definition: interface.h:18
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:147
unsigned short uint16_t
Definition: stdint.h:11
void intf_close(struct interface *intf, int rc)
Close an object interface.
Definition: interface.c:250
#define iob_put(iobuf, len)
Definition: iobuf.h:125
Data transfer metadata.
Definition: xfer.h:23
void intf_shutdown(struct interface *intf, int rc)
Shut down an object interface.
Definition: interface.c:279
struct refcnt refcnt
Reference count.
Definition: pinger.c:60
static void start_timer_nodelay(struct retry_timer *timer)
Start timer with no delay.
Definition: retry.h:100
struct interface xfer
Data transfer interface.
Definition: pinger.c:65
#define XFER_FL_ABS_OFFSET
Offset is absolute.
Definition: xfer.h:48
#define ref_init(refcnt, free)
Initialise a reference counter.
Definition: refcnt.h:65
Error codes.
static int pinger_deliver(struct pinger *pinger, struct io_buffer *iobuf, struct xfer_metadata *meta)
Handle received data.
Definition: pinger.c:224
size_t len
Payload length.
Definition: pinger.c:73
I/O buffers.
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition: iobuf.c:153
static void pinger_window_changed(struct pinger *pinger)
Handle data transfer window change.
Definition: pinger.c:150
Retry timers.
#define EPROTO_DATA
Definition: pinger.c:50
#define DBGC(...)
Definition: compiler.h:505
A retry timer.
Definition: retry.h:22
void intf_plug_plug(struct interface *a, struct interface *b)
Plug two object interfaces together.
Definition: interface.c:108
void(* callback)(struct sockaddr *src, unsigned int sequence, size_t len, int rc)
Callback function.
Definition: pinger.c:90
struct io_buffer * xfer_alloc_iob(struct interface *intf, size_t len)
Allocate I/O buffer.
Definition: xfer.c:159
#define EPROTO_SEQ
Definition: pinger.c:53
int create_pinger(struct interface *job, const char *hostname, unsigned long timeout, size_t len, unsigned int count, void(*callback)(struct sockaddr *src, unsigned int sequence, size_t len, int rc))
Create pinger.
Definition: pinger.c:313
size_t xfer_window(struct interface *intf)
Check flow control window.
Definition: xfer.c:117
uint16_t sequence
Current sequence number.
Definition: pinger.c:75
static void pinger_expired(struct retry_timer *timer, int over __unused)
Handle timer expiry.
Definition: pinger.c:167
Data transfer interfaces.
A reference counter.
Definition: refcnt.h:27
A timer.
Definition: timer.h:29
int rc
Return status.
Definition: pinger.c:81
#define ENOMEM
Not enough space.
Definition: errno.h:535
static int pinger_verify(struct pinger *pinger, const void *data)
Verify payload.
Definition: pinger.c:116
static struct interface_operation pinger_job_op[]
Pinger job control interface operations.
Definition: pinger.c:294
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:36
An object interface.
Definition: interface.h:125
static const void * src
Definition: string.h:48
#define SOCK_ECHO
Definition: socket.h:35
struct interface job
Job control interface.
Definition: pinger.c:63
#define DBGC_HDA(...)
Definition: compiler.h:506
#define __unused
Declare a variable or data structure as unused.
Definition: compiler.h:573
Object interfaces.
ring len
Length.
Definition: dwmac.h:231
ICMP ping sender.
static unsigned int count
Number of entries.
Definition: dwmac.h:225
struct retry_timer timer
Timer.
Definition: pinger.c:68
#define EPROTO_LEN
Definition: pinger.c:47
static void pinger_close(struct pinger *pinger, int rc)
Close pinger.
Definition: pinger.c:135
Generalized socket address structure.
Definition: socket.h:97
An object interface descriptor.
Definition: interface.h:56
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:79
void * zalloc(size_t size)
Allocate cleared memory.
Definition: malloc.c:662
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition: iobuf.h:160
static struct interface_operation pinger_xfer_op[]
Pinger data transfer interface operations.
Definition: pinger.c:283
#define INTF_OP(op_type, object_type, op_func)
Define an object interface operation.
Definition: interface.h:33
int xfer_deliver(struct interface *intf, struct io_buffer *iobuf, struct xfer_metadata *meta)
Deliver datagram.
Definition: xfer.c:195
unsigned char uint8_t
Definition: stdint.h:10
Data transfer interface opening.
Job control interfaces.
unsigned long timeout
Timeout.
Definition: pinger.c:70
void start_timer_fixed(struct retry_timer *timer, unsigned long timeout)
Start timer with a specified timeout.
Definition: retry.c:65
#define INTF_DESC(object_type, intf, operations)
Define an object interface descriptor.
Definition: interface.h:81
void stop_timer(struct retry_timer *timer)
Stop timer.
Definition: retry.c:118
static void pinger_generate(struct pinger *pinger, void *data)
Generate payload.
Definition: pinger.c:100
void * data
Start of data.
Definition: iobuf.h:53
Reference counting.
uint8_t data[48]
Additional event data.
Definition: ena.h:22
void timeout(int)
static struct interface_descriptor pinger_xfer_desc
Pinger data transfer interface descriptor.
Definition: pinger.c:290
FILE_SECBOOT(PERMITTED)
uint8_t meta
Metadata flags.
Definition: ena.h:14
static void intf_init(struct interface *intf, struct interface_descriptor *desc, struct refcnt *refcnt)
Initialise an object interface.
Definition: interface.h:204
Socket addresses.
uint8_t bytes[64]
Definition: ib_mad.h:17
static struct interface_descriptor pinger_job_desc
Pinger job control interface descriptor.
Definition: pinger.c:299
unsigned int remaining
Number of remaining expiry events (zero to continue indefinitely)
Definition: pinger.c:79
int pending
Response for current sequence number is still pending.
Definition: pinger.c:77
#define NULL
NULL pointer (VOID *)
Definition: Base.h:322
#define ETIMEDOUT
Connection timed out.
Definition: errno.h:670
String functions.
A pinger.
Definition: pinger.c:58
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:403
#define ref_put(refcnt)
Drop reference to object.
Definition: refcnt.h:107
void * memset(void *dest, int character, size_t len) __nonnull
A persistent I/O buffer.
Definition: iobuf.h:38