From 1c7f47895c791f9476e641308741879480742ef4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 20 Sep 2010 13:04:17 +0100 Subject: [PATCH] [lotest] Add loopback testing commands Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/general.h | 1 + src/hci/commands/lotest_cmd.c | 114 ++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/usr/lotest.c | 210 ++++++++++++++++++++++++++++++++++ src/usr/lotest.h | 15 +++ 6 files changed, 344 insertions(+) create mode 100644 src/hci/commands/lotest_cmd.c create mode 100644 src/usr/lotest.c create mode 100644 src/usr/lotest.h diff --git a/src/config/config.c b/src/config/config.c index 46df6bd2..f9061d06 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -231,6 +231,9 @@ REQUIRE_OBJECT ( digest_cmd ); #ifdef PXE_CMD REQUIRE_OBJECT ( pxe_cmd ); #endif +#ifdef LOTEST_CMD +REQUIRE_OBJECT ( lotest_cmd ); +#endif /* * Drag in miscellaneous objects diff --git a/src/config/general.h b/src/config/general.h index dbb8b426..652ecf75 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -122,6 +122,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define LOGIN_CMD /* Login command */ #undef TIME_CMD /* Time commands */ #undef DIGEST_CMD /* Image crypto digest commands */ +#undef LOTEST_CMD /* Loopback testing commands */ //#undef PXE_CMD /* PXE commands */ /* diff --git a/src/hci/commands/lotest_cmd.c b/src/hci/commands/lotest_cmd.c new file mode 100644 index 00000000..a2c52bc1 --- /dev/null +++ b/src/hci/commands/lotest_cmd.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Loopback testing commands + * + */ + +static void lotest_syntax ( char **argv ) { + printf ( "Usage:\n %s \n", + argv[0] ); +} + +static int lotest_exec ( int argc, char **argv ) { + static struct option lotest_opts[] = { + { "help", 0, NULL, 'h' }, + { "mtu", required_argument, NULL, 'm' }, + { NULL, 0, NULL, 0 }, + }; + const char *sender_name; + const char *receiver_name; + const char *mtu_text = NULL; + struct net_device *sender; + struct net_device *receiver; + char *endp; + size_t mtu; + int c; + int rc; + + /* Parse command line */ + while ( ( c = getopt_long ( argc, argv, "hm:", lotest_opts, + NULL ) ) >= 0 ) { + switch ( c ) { + case 'm': + mtu_text = optarg; + break; + case 'h': + /* Display help text */ + default: + /* Unrecognised/invalid option */ + lotest_syntax ( argv ); + return 1; + } + } + if ( optind != ( argc - 2 ) ) { + lotest_syntax ( argv ); + return 1; + } + sender_name = argv[optind]; + receiver_name = argv[optind + 1]; + + /* Identify network devices */ + sender = find_netdev ( sender_name ); + if ( ! sender ) { + printf ( "%s: no such interface\n", sender_name ); + return 1; + } + receiver = find_netdev ( receiver_name ); + if ( ! receiver ) { + printf ( "%s: no such interface\n", receiver_name ); + return 1; + } + + /* Identify MTU */ + if ( mtu_text ) { + mtu = strtoul ( mtu_text, &endp, 10 ); + if ( *endp ) { + printf ( "%s: invalid MTU\n", mtu_text ); + return 1; + } + } else { + mtu = ETH_MAX_MTU; + } + + /* Perform loopback test */ + if ( ( rc = loopback_test ( sender, receiver, mtu ) ) != 0 ) { + printf ( "Test failed: %s\n", strerror ( rc ) ); + return 1; + } + + return 0; +} + +struct command lotest_command __command = { + .name = "lotest", + .exec = lotest_exec, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index efa3089f..8101560c 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -223,6 +223,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ib_srpboot ( ERRFILE_OTHER | 0x00180000 ) #define ERRFILE_iwmgmt ( ERRFILE_OTHER | 0x00190000 ) #define ERRFILE_linux_smbios ( ERRFILE_OTHER | 0x001a0000 ) +#define ERRFILE_lotest ( ERRFILE_OTHER | 0x001b0000 ) /** @} */ diff --git a/src/usr/lotest.c b/src/usr/lotest.c new file mode 100644 index 00000000..8bbda51a --- /dev/null +++ b/src/usr/lotest.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2010 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Loopback testing + * + */ + +#define LINK_WAIT_MS 15000 + +/** + * Process received packet + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @ret rc Return status code + */ +static int lotest_rx ( struct io_buffer *iobuf, + struct net_device *netdev __unused, + const void *ll_source __unused ) { + free_iob ( iobuf ); + return -ENOTSUP; +} + +/** + * Transcribe network-layer address + * + * @v net_addr Network-layer address + * @ret string Human-readable transcription of address + */ +static const char * lotest_ntoa ( const void *net_addr __unused ) { + return ""; +} + +/** + * Loopback test network-layer protocol + * + * Using a dedicated network-layer protocol avoids problems caused by + * cards supporting features such as IPv4 checksum offload trying to + * interpret the (randomly generated) network-layer content. + */ +static struct net_protocol lotest_protocol __net_protocol = { + .name = "LOTEST", + .rx = lotest_rx, + .ntoa = lotest_ntoa, + .net_proto = htons ( 0x6950 ), /* Not a genuine protocol number */ + .net_addr_len = 0, +}; + +/** + * Perform loopback test between two network devices + * + * @v sender Sending network device + * @v receiver Received network device + * @v mtu Packet size (excluding link-layer headers) + * @ret rc Return status code + */ +int loopback_test ( struct net_device *sender, struct net_device *receiver, + size_t mtu ) { + uint8_t buf[mtu]; + struct io_buffer *iobuf; + const void *ll_dest; + const void *ll_source; + uint16_t net_proto; + unsigned int i; + unsigned int successes; + int rc; + + /* Open network devices */ + if ( ( rc = ifopen ( sender ) ) != 0 ) + return rc; + if ( ( rc = ifopen ( receiver ) ) != 0 ) + return rc; + + /* Wait for link-up */ + if ( ( rc = iflinkwait ( sender, LINK_WAIT_MS ) ) != 0 ) + return rc; + if ( ( rc = iflinkwait ( receiver, LINK_WAIT_MS ) ) != 0 ) + return rc; + + /* Print initial statistics */ + printf ( "Performing loopback test from %s to %s with %zd byte MTU\n", + sender->name, receiver->name, mtu ); + ifstat ( sender ); + ifstat ( receiver ); + + /* Perform loopback test */ + for ( successes = 0 ; ; successes++ ) { + + /* Print running total */ + printf ( "\r%d", successes ); + + /* Generate random packet */ + for ( i = 0 ; i < sizeof ( buf ) ; i++ ) + buf[i] = random(); + iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( buf ) ); + if ( ! iobuf ) { + printf ( "\nFailed to allocate I/O buffer" ); + rc = -ENOMEM; + goto done; + } + iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); + memcpy ( iob_put ( iobuf, sizeof ( buf ) ), + buf, sizeof ( buf ) ); + + /* Transmit packet */ + if ( ( rc = net_tx ( iob_disown ( iobuf ), sender, + &lotest_protocol, + receiver->ll_addr ) ) != 0 ) { + printf ( "\nFailed to transmit packet: %s", + strerror ( rc ) ); + goto done; + } + + /* Poll until packet arrives */ + do { + /* Check for cancellation */ + if ( iskey() && ( getchar() == CTRL_C ) ) { + rc = -ECANCELED; + goto done; + } + /* Poll network devices */ + netdev_poll ( sender ); + netdev_poll ( receiver ); + } while ( ( iobuf = netdev_rx_dequeue ( receiver ) ) == NULL ); + + /* Check received packet */ + if ( ( rc = receiver->ll_protocol->pull ( receiver, iobuf, + &ll_dest, &ll_source, + &net_proto ) ) != 0 ){ + printf ( "\nFailed to strip link-layer header: %s", + strerror ( rc ) ); + goto done; + } + if ( net_proto == lotest_protocol.net_proto ) { + if ( iob_len ( iobuf ) != sizeof ( buf ) ) { + printf ( "\nLength mismatch: sent %zd, " + "received %zd", + sizeof ( buf ), iob_len ( iobuf ) ); + DBG ( "\nSent:\n" ); + DBG_HDA ( 0, buf, sizeof ( buf ) ); + DBG ( "Received:\n" ); + DBG_HDA ( 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + if ( memcmp ( iobuf->data, buf, sizeof ( buf ) ) != 0){ + printf ( "\nContent mismatch" ); + DBG ( "\nSent:\n" ); + DBG_HDA ( 0, buf, sizeof ( buf ) ); + DBG ( "Received:\n" ); + DBG_HDA ( 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + } else { + printf ( "\nReceived spurious packet type %04x\n", + net_proto ); + /* Continue; this allows for the fact that + * there may have been packets outstanding on + * the wire when we started the test. + */ + } + + free_iob ( iob_disown ( iobuf ) ); + } + + done: + printf ( "\n"); + free_iob ( iobuf ); + + /* Dump final statistics */ + ifstat ( sender ); + ifstat ( receiver ); + + return 0; +} diff --git a/src/usr/lotest.h b/src/usr/lotest.h new file mode 100644 index 00000000..aa4bbac4 --- /dev/null +++ b/src/usr/lotest.h @@ -0,0 +1,15 @@ +#ifndef _USR_LOTEST_H +#define _USR_LOTEST_H + +/** @file + * + * Loopback testing + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +extern int loopback_test ( struct net_device *sender, + struct net_device *receiver, size_t mtu ); + +#endif /* _USR_LOTEST_H */