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