~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/core/pinger.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

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