2 * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
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.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL
);
32 #include <ipxe/iobuf.h>
33 #include <ipxe/netdevice.h>
34 #include <ipxe/if_ether.h>
35 #include <ipxe/keys.h>
36 #include <ipxe/console.h>
37 #include <usr/ifmgmt.h>
38 #include <usr/lotest.h>
46 /** Current loopback test receiver */
47 static struct net_device
*lotest_receiver
;
49 /** Loopback testing received packets */
50 static LIST_HEAD ( lotest_queue
);
53 * Process received packet
56 * @v netdev Network device
57 * @v ll_dest Link-layer destination address
58 * @v ll_source Link-layer source address
59 * @v flags Packet flags
60 * @ret rc Return status code
62 static int lotest_rx ( struct io_buffer
*iobuf
,
63 struct net_device
*netdev
,
64 const void *ll_dest __unused
,
65 const void *ll_source __unused
,
66 unsigned int flags __unused
) {
68 /* Add to received packet queue if currently performing a test */
69 if ( netdev
== lotest_receiver
) {
70 list_add_tail ( &iobuf
->list
, &lotest_queue
);
79 * Dequeue received packet
81 * @ret iobuf I/O buffer, or NULL
83 static struct io_buffer
* lotest_dequeue ( void ) {
84 struct io_buffer
*iobuf
;
86 /* Remove first packet (if any) from received packet queue */
87 iobuf
= list_first_entry ( &lotest_queue
, struct io_buffer
, list
);
90 list_del ( &iobuf
->list
);
96 * Transcribe network-layer address
98 * @v net_addr Network-layer address
99 * @ret string Human-readable transcription of address
101 static const char * lotest_ntoa ( const void *net_addr __unused
) {
106 * Loopback test network-layer protocol
108 * Using a dedicated network-layer protocol avoids problems caused by
109 * cards supporting features such as IPv4 checksum offload trying to
110 * interpret the (randomly generated) network-layer content.
112 static struct net_protocol lotest_protocol __net_protocol
= {
116 .net_proto
= htons ( 0x6950 ), /* Not a genuine protocol number */
121 * Discard all received loopback test packets
124 static void lotest_flush ( void ) {
125 struct io_buffer
*iobuf
;
127 while ( ( iobuf
= lotest_dequeue() ) != NULL
)
132 * Wait for packet to be received
134 * @v data Expected data
135 * @v len Expected data length
136 * @ret rc Return status code
138 static int loopback_wait ( void *data
, size_t len
) {
139 struct io_buffer
*iobuf
;
141 /* Poll until packet arrives */
144 /* Check for cancellation */
145 if ( iskey() && ( getchar() == CTRL_C
) )
148 /* Poll network devices */
151 /* Dequeue packet, if available */
152 iobuf
= lotest_dequeue();
156 /* Check packet length */
157 if ( iob_len ( iobuf
) != len
) {
158 printf ( "\nLength mismatch: sent %zd, received %zd",
159 len
, iob_len ( iobuf
) );
161 DBG_HDA ( 0, data
, len
);
162 DBG ( "Received:\n" );
163 DBG_HDA ( 0, iobuf
->data
, iob_len ( iobuf
) );
164 free_iob ( iob_disown ( iobuf
) );
168 /* Check packet content */
169 if ( memcmp ( iobuf
->data
, data
, len
) != 0 ) {
170 printf ( "\nContent mismatch" );
172 DBG_HDA ( 0, data
, len
);
173 DBG ( "Received:\n" );
174 DBG_HDA ( 0, iobuf
->data
, iob_len ( iobuf
) );
175 free_iob ( iob_disown ( iobuf
) );
179 /* Discard packet and return */
180 free_iob ( iob_disown ( iobuf
) );
186 * Perform loopback test between two network devices
188 * @v sender Sending network device
189 * @v receiver Received network device
190 * @v mtu Packet size (excluding link-layer headers)
191 * @ret rc Return status code
193 int loopback_test ( struct net_device
*sender
, struct net_device
*receiver
,
197 struct io_buffer
*iobuf
;
199 unsigned int successes
;
202 /* Open network devices */
203 if ( ( rc
= ifopen ( sender
) ) != 0 )
205 if ( ( rc
= ifopen ( receiver
) ) != 0 )
208 /* Wait for link-up */
209 if ( ( rc
= iflinkwait ( sender
, 0 ) ) != 0 )
211 if ( ( rc
= iflinkwait ( receiver
, 0 ) ) != 0 )
214 /* Allocate data buffer */
215 if ( mtu
< sizeof ( *seq
) )
216 mtu
= sizeof ( *seq
);
217 buf
= malloc ( mtu
);
220 seq
= ( ( void * ) buf
);
222 /* Print initial statistics */
223 printf ( "Performing loopback test from %s to %s with %zd byte MTU\n",
224 sender
->name
, receiver
->name
, mtu
);
228 /* Start loopback test */
230 lotest_receiver
= receiver
;
232 /* Perform loopback test */
233 for ( successes
= 0 ; ; successes
++ ) {
235 /* Print running total */
236 printf ( "\r%d", successes
);
238 /* Generate random packet */
239 *seq
= htonl ( successes
);
240 for ( i
= sizeof ( *seq
) ; i
< mtu
; i
++ )
242 iobuf
= alloc_iob ( MAX_LL_HEADER_LEN
+ mtu
);
244 printf ( "\nFailed to allocate I/O buffer" );
248 iob_reserve ( iobuf
, MAX_LL_HEADER_LEN
);
249 memcpy ( iob_put ( iobuf
, mtu
), buf
, mtu
);
251 /* Transmit packet */
252 if ( ( rc
= net_tx ( iob_disown ( iobuf
), sender
,
253 &lotest_protocol
, receiver
->ll_addr
,
254 sender
->ll_addr
) ) != 0 ) {
255 printf ( "\nFailed to transmit packet: %s",
260 /* Wait for received packet */
261 if ( ( rc
= loopback_wait ( buf
, mtu
) ) != 0 )
267 /* Stop loopback testing */
268 lotest_receiver
= NULL
;
271 /* Dump final statistics */