1 /*
2 * Copyright (C) 2010 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 <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <byteswap.h>
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>
39
40 /** @file
41 *
42 * Loopback testing
43 *
44 */
45
46 /** Current loopback test receiver */
47 static struct net_device *lotest_receiver;
48
49 /** Loopback testing received packets */
50 static LIST_HEAD ( lotest_queue );
51
52 /**
53 * Process received packet
54 *
55 * @v iobuf I/O buffer
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
61 */
lotest_rx(struct io_buffer * iobuf,struct net_device * netdev,const void * ll_dest __unused,const void * ll_source __unused,unsigned int flags __unused)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 ) {
67
68 /* Add to received packet queue if currently performing a test */
69 if ( netdev == lotest_receiver ) {
70 list_add_tail ( &iobuf->list, &lotest_queue );
71 } else {
72 free_iob ( iobuf );
73 }
74
75 return 0;
76 }
77
78 /**
79 * Dequeue received packet
80 *
81 * @ret iobuf I/O buffer, or NULL
82 */
lotest_dequeue(void)83 static struct io_buffer * lotest_dequeue ( void ) {
84 struct io_buffer *iobuf;
85
86 /* Remove first packet (if any) from received packet queue */
87 iobuf = list_first_entry ( &lotest_queue, struct io_buffer, list );
88 if ( ! iobuf )
89 return NULL;
90 list_del ( &iobuf->list );
91
92 return iobuf;
93 }
94
95 /**
96 * Transcribe network-layer address
97 *
98 * @v net_addr Network-layer address
99 * @ret string Human-readable transcription of address
100 */
lotest_ntoa(const void * net_addr __unused)101 static const char * lotest_ntoa ( const void *net_addr __unused ) {
102 return "<INVALID>";
103 }
104
105 /**
106 * Loopback test network-layer protocol
107 *
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.
111 */
112 static struct net_protocol lotest_protocol __net_protocol = {
113 .name = "LOTEST",
114 .rx = lotest_rx,
115 .ntoa = lotest_ntoa,
116 .net_proto = htons ( 0x6950 ), /* Not a genuine protocol number */
117 .net_addr_len = 0,
118 };
119
120 /**
121 * Discard all received loopback test packets
122 *
123 */
lotest_flush(void)124 static void lotest_flush ( void ) {
125 struct io_buffer *iobuf;
126
127 while ( ( iobuf = lotest_dequeue() ) != NULL )
128 free_iob ( iobuf );
129 }
130
131 /**
132 * Wait for packet to be received
133 *
134 * @v data Expected data
135 * @v len Expected data length
136 * @ret rc Return status code
137 */
loopback_wait(void * data,size_t len)138 static int loopback_wait ( void *data, size_t len ) {
139 struct io_buffer *iobuf;
140
141 /* Poll until packet arrives */
142 while ( 1 ) {
143
144 /* Check for cancellation */
145 if ( iskey() && ( getchar() == CTRL_C ) )
146 return -ECANCELED;
147
148 /* Poll network devices */
149 net_poll();
150
151 /* Dequeue packet, if available */
152 iobuf = lotest_dequeue();
153 if ( ! iobuf )
154 continue;
155
156 /* Check packet length */
157 if ( iob_len ( iobuf ) != len ) {
158 printf ( "\nLength mismatch: sent %zd, received %zd",
159 len, iob_len ( iobuf ) );
160 DBG ( "\nSent:\n" );
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 ) );
165 return -EINVAL;
166 }
167
168 /* Check packet content */
169 if ( memcmp ( iobuf->data, data, len ) != 0 ) {
170 printf ( "\nContent mismatch" );
171 DBG ( "\nSent:\n" );
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 ) );
176 return -EINVAL;
177 }
178
179 /* Discard packet and return */
180 free_iob ( iob_disown ( iobuf ) );
181 return 0;
182 }
183 }
184
185 /**
186 * Perform loopback test between two network devices
187 *
188 * @v sender Sending network device
189 * @v receiver Received network device
190 * @v mtu Packet size (excluding link-layer headers)
191 * @v broadcast Use broadcast link-layer address
192 * @ret rc Return status code
193 */
loopback_test(struct net_device * sender,struct net_device * receiver,size_t mtu,int broadcast)194 int loopback_test ( struct net_device *sender, struct net_device *receiver,
195 size_t mtu, int broadcast ) {
196 uint8_t *buf;
197 uint32_t *seq;
198 struct io_buffer *iobuf;
199 const void *ll_dest;
200 unsigned int i;
201 unsigned int successes;
202 int rc;
203
204 /* Open network devices */
205 if ( ( rc = ifopen ( sender ) ) != 0 )
206 return rc;
207 if ( ( rc = ifopen ( receiver ) ) != 0 )
208 return rc;
209
210 /* Wait for link-up */
211 if ( ( rc = iflinkwait ( sender, 0 ) ) != 0 )
212 return rc;
213 if ( ( rc = iflinkwait ( receiver, 0 ) ) != 0 )
214 return rc;
215
216 /* Allocate data buffer */
217 if ( mtu < sizeof ( *seq ) )
218 mtu = sizeof ( *seq );
219 buf = malloc ( mtu );
220 if ( ! buf )
221 return -ENOMEM;
222 seq = ( ( void * ) buf );
223
224 /* Determine destination address */
225 ll_dest = ( broadcast ? sender->ll_broadcast : receiver->ll_addr );
226
227 /* Print initial statistics */
228 printf ( "Performing %sloopback test from %s to %s with %zd byte MTU\n",
229 ( broadcast ? "broadcast " : "" ), sender->name,
230 receiver->name, mtu );
231 ifstat ( sender );
232 ifstat ( receiver );
233
234 /* Start loopback test */
235 lotest_flush();
236 lotest_receiver = receiver;
237
238 /* Perform loopback test */
239 for ( successes = 0 ; ; successes++ ) {
240
241 /* Print running total */
242 printf ( "\r%d", successes );
243
244 /* Generate random packet */
245 *seq = htonl ( successes );
246 for ( i = sizeof ( *seq ) ; i < mtu ; i++ )
247 buf[i] = random();
248 iobuf = alloc_iob ( MAX_LL_HEADER_LEN + mtu );
249 if ( ! iobuf ) {
250 printf ( "\nFailed to allocate I/O buffer" );
251 rc = -ENOMEM;
252 break;
253 }
254 iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
255 memcpy ( iob_put ( iobuf, mtu ), buf, mtu );
256
257 /* Transmit packet */
258 if ( ( rc = net_tx ( iob_disown ( iobuf ), sender,
259 &lotest_protocol, ll_dest,
260 sender->ll_addr ) ) != 0 ) {
261 printf ( "\nFailed to transmit packet: %s",
262 strerror ( rc ) );
263 break;
264 }
265
266 /* Wait for received packet */
267 if ( ( rc = loopback_wait ( buf, mtu ) ) != 0 )
268 break;
269 }
270
271 printf ( "\n");
272
273 /* Stop loopback testing */
274 lotest_receiver = NULL;
275 lotest_flush();
276
277 /* Dump final statistics */
278 ifstat ( sender );
279 ifstat ( receiver );
280
281 /* Free buffer */
282 free ( buf );
283
284 return 0;
285 }
286