From 958ddda00522f768bf9adc86c0d5b446e0a672cf Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 13 Jun 2008 16:20:05 -0700 Subject: [PATCH] gpxe: update gpxe to latest git --- gpxe/src/Makefile | 2 +- gpxe/src/Makefile.housekeeping | 4 +- gpxe/src/arch/i386/drivers/net/undinet.c | 8 +- gpxe/src/arch/i386/firmware/pcbios/smbios.c | 6 +- gpxe/src/arch/i386/image/bzimage.c | 12 +- gpxe/src/arch/i386/image/elfboot.c | 110 ++++ gpxe/src/arch/i386/include/bits/errfile.h | 1 + gpxe/src/arch/i386/include/bzimage.h | 8 + gpxe/src/config.h | 3 +- gpxe/src/core/config.c | 5 +- gpxe/src/core/console.c | 14 +- gpxe/src/drivers/net/via-rhine.c | 6 +- gpxe/src/hci/commands/image_cmd.c | 25 +- gpxe/src/image/elf.c | 29 +- gpxe/src/include/gpxe/aoe.h | 1 - gpxe/src/include/gpxe/async.h | 228 -------- gpxe/src/include/gpxe/errfile.h | 1 + gpxe/src/include/gpxe/features.h | 2 + gpxe/src/include/gpxe/in.h | 9 + gpxe/src/include/gpxe/retry.h | 14 +- gpxe/src/net/retry.c | 9 +- gpxe/src/net/udp.c | 51 +- gpxe/src/net/udp/dhcp.c | 10 +- gpxe/src/net/udp/slam.c | 810 ++++++++++++++++++++++++++++ gpxe/src/proto/slam.c | 541 ------------------- 25 files changed, 1053 insertions(+), 856 deletions(-) create mode 100644 gpxe/src/arch/i386/image/elfboot.c delete mode 100644 gpxe/src/include/gpxe/async.h create mode 100644 gpxe/src/net/udp/slam.c delete mode 100644 gpxe/src/proto/slam.c diff --git a/gpxe/src/Makefile b/gpxe/src/Makefile index c0b29ee..26d9ee8 100644 --- a/gpxe/src/Makefile +++ b/gpxe/src/Makefile @@ -45,7 +45,7 @@ DOXYGEN := doxygen # If invoked with no build target, print out a helpfully suggestive # message. # -noargs : blib $(BIN)/NIC $(BIN)/gpxe.dsk $(BIN)/gpxe.iso $(BIN)/gpxe.usb +noargs : blib $(BIN)/NIC $(BIN)/gpxe.dsk $(BIN)/gpxe.iso $(BIN)/gpxe.usb $(BIN)/undionly.kpxe @$(ECHO) '===========================================================' @$(ECHO) @$(ECHO) 'To create a bootable floppy, type' diff --git a/gpxe/src/Makefile.housekeeping b/gpxe/src/Makefile.housekeeping index bbea2a5..53e02e3 100644 --- a/gpxe/src/Makefile.housekeeping +++ b/gpxe/src/Makefile.housekeeping @@ -12,7 +12,7 @@ CLEANUP := $(BIN)/*.* # *.* to avoid catching the "CVS" directory VERSION_MAJOR = 0 VERSION_MINOR = 9 VERSION_PATCH = 3 -EXTRAVERSION = +EXTRAVERSION = + MM_VERSION = $(VERSION_MAJOR).$(VERSION_MINOR) VERSION = $(MM_VERSION).$(VERSION_PATCH)$(EXTRAVERSION) CFLAGS += -DVERSION_MAJOR=$(VERSION_MAJOR) \ @@ -371,7 +371,7 @@ $(BIN)/%.tmp : $(BLIB) $(MAKEDEPS) $(LDSCRIPT) $(Q)$(OBJDUMP) -ht $@ | $(SORTOBJDUMP) >> $(BIN)/$*.tmp.map # Keep intermediate object file (useful for debugging) -.SECONDARY : $(BIN)/%.tmp +.PRECIOUS : $(BIN)/%.tmp # Show a linker map for the specified target # diff --git a/gpxe/src/arch/i386/drivers/net/undinet.c b/gpxe/src/arch/i386/drivers/net/undinet.c index 2c66c2f..b1e8cc5 100644 --- a/gpxe/src/arch/i386/drivers/net/undinet.c +++ b/gpxe/src/arch/i386/drivers/net/undinet.c @@ -543,9 +543,13 @@ static int undinet_open ( struct net_device *netdev ) { undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS, &undi_set_address, sizeof ( undi_set_address ) ); - /* Open NIC */ + /* Open NIC. We ask for promiscuous operation, since it's the + * only way to ask for all multicast addresses. On any + * switched network, it shouldn't really make a difference to + * performance. + */ memset ( &undi_open, 0, sizeof ( undi_open ) ); - undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST ); + undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS ); if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open, sizeof ( undi_open ) ) ) != 0 ) goto err; diff --git a/gpxe/src/arch/i386/firmware/pcbios/smbios.c b/gpxe/src/arch/i386/firmware/pcbios/smbios.c index aa4f3f3..875d421 100644 --- a/gpxe/src/arch/i386/firmware/pcbios/smbios.c +++ b/gpxe/src/arch/i386/firmware/pcbios/smbios.c @@ -275,14 +275,12 @@ int read_smbios_string ( struct smbios_structure *structure, * smbios_strings struct is constructed so as to * always end on a string boundary. */ - string_len = strlen_user ( smbios.address, - ( structure->offset + offset ) ); + string_len = strlen_user ( smbios.address, offset ); if ( --index == 0 ) { /* Copy string, truncating as necessary. */ if ( len > string_len ) len = string_len; - copy_from_user ( data, smbios.address, - ( structure->offset + offset ), len ); + copy_from_user ( data, smbios.address, offset, len ); return string_len; } } diff --git a/gpxe/src/arch/i386/image/bzimage.c b/gpxe/src/arch/i386/image/bzimage.c index ed9d286..38443f5 100644 --- a/gpxe/src/arch/i386/image/bzimage.c +++ b/gpxe/src/arch/i386/image/bzimage.c @@ -76,6 +76,8 @@ struct bzimage_exec_context { size_t rm_heap; /** Command line (offset from rm_kernel) */ size_t rm_cmdline; + /** Command line maximum length */ + size_t cmdline_size; /** Video mode */ unsigned int vid_mode; /** Memory limit */ @@ -162,8 +164,8 @@ static int bzimage_set_cmdline ( struct image *image, /* Copy command line down to real-mode portion */ cmdline_len = ( strlen ( cmdline ) + 1 ); - if ( cmdline_len > BZI_CMDLINE_SIZE ) - cmdline_len = BZI_CMDLINE_SIZE; + if ( cmdline_len > exec_ctx->cmdline_size ) + cmdline_len = exec_ctx->cmdline_size; copy_to_user ( exec_ctx->rm_kernel, exec_ctx->rm_cmdline, cmdline, cmdline_len ); DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline ); @@ -320,6 +322,12 @@ static int bzimage_exec ( struct image *image ) { } else { exec_ctx.mem_limit = BZI_INITRD_MAX; } + if ( bzhdr.version >= 0x0206 ) { + exec_ctx.cmdline_size = bzhdr.cmdline_size; + } else { + exec_ctx.cmdline_size = BZI_CMDLINE_SIZE; + } + DBG ( "cmdline_size = %zd\n", exec_ctx.cmdline_size ); /* Parse command line for bootloader parameters */ if ( ( rc = bzimage_parse_cmdline ( image, &exec_ctx, cmdline ) ) != 0) diff --git a/gpxe/src/arch/i386/image/elfboot.c b/gpxe/src/arch/i386/image/elfboot.c new file mode 100644 index 0000000..52510aa --- /dev/null +++ b/gpxe/src/arch/i386/image/elfboot.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @file + * + * ELF bootable image + * + */ + +FEATURE ( FEATURE_IMAGE, "ELF", DHCP_EB_FEATURE_ELF, 1 ); + +struct image_type elfboot_image_type __image_type ( PROBE_NORMAL ); + +/** + * Execute ELF image + * + * @v image ELF image + * @ret rc Return status code + */ +static int elfboot_exec ( struct image *image ) { + physaddr_t entry = image->priv.phys; + + /* An ELF image has no callback interface, so we need to shut + * down before invoking it. + */ + shutdown(); + + /* Jump to OS with flat physical addressing */ + __asm__ __volatile__ ( PHYS_CODE ( "call *%%edi\n\t" ) + : : "D" ( entry ) + : "eax", "ebx", "ecx", "edx", "esi", "ebp", + "memory" ); + + DBGC ( image, "ELF %p returned\n", image ); + + /* It isn't safe to continue after calling shutdown() */ + while ( 1 ) {} + + return -ECANCELED; /* -EIMPOSSIBLE, anyone? */ +} + +/** + * Load ELF image into memory + * + * @v image ELF file + * @ret rc Return status code + */ +static int elfboot_load ( struct image *image ) { + Elf32_Ehdr ehdr; + static const uint8_t e_ident[] = { + [EI_MAG0] = ELFMAG0, + [EI_MAG1] = ELFMAG1, + [EI_MAG2] = ELFMAG2, + [EI_MAG3] = ELFMAG3, + [EI_CLASS] = ELFCLASS32, + [EI_DATA] = ELFDATA2LSB, + [EI_VERSION] = EV_CURRENT, + }; + int rc; + + /* Read ELF header */ + copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); + if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) { + DBG ( "Invalid ELF identifier\n" ); + return -ENOEXEC; + } + + /* This is an ELF image, valid or otherwise */ + if ( ! image->type ) + image->type = &elfboot_image_type; + + /* Load the image using core ELF support */ + if ( ( rc = elf_load ( image ) ) != 0 ) { + DBGC ( image, "ELF %p could not load: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** ELF image type */ +struct image_type elfboot_image_type __image_type ( PROBE_NORMAL ) = { + .name = "ELF", + .load = elfboot_load, + .exec = elfboot_exec, +}; diff --git a/gpxe/src/arch/i386/include/bits/errfile.h b/gpxe/src/arch/i386/include/bits/errfile.h index 678be04..c5b04a2 100644 --- a/gpxe/src/arch/i386/include/bits/errfile.h +++ b/gpxe/src/arch/i386/include/bits/errfile.h @@ -20,6 +20,7 @@ #define ERRFILE_multiboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00030000 ) #define ERRFILE_nbi ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00040000 ) #define ERRFILE_pxe_image ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00050000 ) +#define ERRFILE_elfboot ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00060000 ) #define ERRFILE_undi ( ERRFILE_ARCH | ERRFILE_NET | 0x00000000 ) #define ERRFILE_undiload ( ERRFILE_ARCH | ERRFILE_NET | 0x00010000 ) diff --git a/gpxe/src/arch/i386/include/bzimage.h b/gpxe/src/arch/i386/include/bzimage.h index 609e836..aee058a 100644 --- a/gpxe/src/arch/i386/include/bzimage.h +++ b/gpxe/src/arch/i386/include/bzimage.h @@ -62,6 +62,14 @@ struct bzimage_header { uint32_t cmd_line_ptr; /** Highest legal initrd address */ uint32_t initrd_addr_max; + /** Physical addr alignment required for kernel */ + uint32_t kernel_alignment; + /** Whether kernel is relocatable or not */ + uint8_t relocatable_kernel; + /** Unused */ + uint8_t pad2[3]; + /** Maximum size of the kernel command line */ + uint32_t cmdline_size; } __attribute__ (( packed )); /** Offset of bzImage header within kernel image */ diff --git a/gpxe/src/config.h b/gpxe/src/config.h index 2d0980b..e695b01 100644 --- a/gpxe/src/config.h +++ b/gpxe/src/config.h @@ -121,8 +121,7 @@ * */ #undef IMAGE_NBI /* NBI image support */ -#undef IMAGE_ELF64 /* ELF64 image support */ -#undef IMAGE_ELF /* ELF image support */ +#define IMAGE_ELF /* ELF image support */ #undef IMAGE_FREEBSD /* FreeBSD kernel image support */ #define IMAGE_MULTIBOOT /* MultiBoot image support */ #undef IMAGE_AOUT /* a.out image support */ diff --git a/gpxe/src/core/config.c b/gpxe/src/core/config.c index 01f709c..018f084 100644 --- a/gpxe/src/core/config.c +++ b/gpxe/src/core/config.c @@ -131,11 +131,8 @@ REQUIRE_OBJECT ( nmb ); #ifdef IMAGE_NBI REQUIRE_OBJECT ( nbi ); #endif -#ifdef IMAGE_ELF64 -REQUIRE_OBJECT ( elf64 ); -#endif #ifdef IMAGE_ELF -REQUIRE_OBJECT ( elf ); +REQUIRE_OBJECT ( elfboot ); #endif #ifdef IMAGE_FREEBSD REQUIRE_OBJECT ( freebsd ); diff --git a/gpxe/src/core/console.c b/gpxe/src/core/console.c index 6f55d55..653f689 100644 --- a/gpxe/src/core/console.c +++ b/gpxe/src/core/console.c @@ -88,9 +88,15 @@ static struct console_driver * has_input ( void ) { */ int getchar ( void ) { struct console_driver *console; - int character = 256; + int character; + + while ( 1 ) { + console = has_input(); + if ( console && console->getchar ) { + character = console->getchar (); + break; + } - while ( character == 256 ) { /* Doze for a while (until the next interrupt). This works * fine, because the keyboard is interrupt-driven, and the * timer interrupt (approx. every 50msec) takes care of the @@ -105,10 +111,6 @@ int getchar ( void ) { * input. */ step(); - - console = has_input(); - if ( console && console->getchar ) - character = console->getchar (); } /* CR -> LF translation */ diff --git a/gpxe/src/drivers/net/via-rhine.c b/gpxe/src/drivers/net/via-rhine.c index 8135009..fb43a92 100644 --- a/gpxe/src/drivers/net/via-rhine.c +++ b/gpxe/src/drivers/net/via-rhine.c @@ -1179,9 +1179,9 @@ rhine_disable ( struct nic *nic ) { printf ("rhine disable\n"); /* Switch to loopback mode to avoid hardware races. */ - writeb(0x60 | 0x01, byTCR); + outb(0x60 | 0x01, byTCR); /* Stop the chip's Tx and Rx processes. */ - writew(CR_STOP, byCR0); + outw(CR_STOP, byCR0); } /************************************************************************** @@ -1279,7 +1279,7 @@ rhine_reset (struct nic *nic) outw (0, byIMR0); } /* Beware of PCI posted writes */ -#define IOSYNC do { readb(nic->ioaddr + StationAddr); } while (0) +#define IOSYNC do { inb(nic->ioaddr + StationAddr); } while (0) static int rhine_poll (struct nic *nic, int retreive) diff --git a/gpxe/src/hci/commands/image_cmd.c b/gpxe/src/hci/commands/image_cmd.c index 44689dd..23843a7 100644 --- a/gpxe/src/hci/commands/image_cmd.c +++ b/gpxe/src/hci/commands/image_cmd.c @@ -49,16 +49,25 @@ enum image_action { */ static int imgfill_cmdline ( struct image *image, unsigned int nargs, char **args ) { - char buf[256]; - size_t used = 0; + size_t len; + unsigned int i; - memset ( buf, 0, sizeof ( buf ) ); - while ( ( used < sizeof ( buf ) ) && nargs-- ) { - used += snprintf ( &buf[used], ( sizeof ( buf ) - used ), - " %s", *(args++) ); - } + /* Determine total length of command line */ + len = 1; /* NUL */ + for ( i = 0 ; i < nargs ; i++ ) + len += ( 1 /* space */ + strlen ( args[i] ) ); + + { + char buf[len]; + char *ptr = buf; - return image_set_cmdline ( image, &buf[1] ); + /* Assemble command line */ + for ( i = 0 ; i < nargs ; i++ ) + ptr += sprintf ( ptr, " %s", args[i] ); + assert ( ptr == ( buf + len - 1 ) ); + + return image_set_cmdline ( image, &buf[1] ); + } } /** diff --git a/gpxe/src/image/elf.c b/gpxe/src/image/elf.c index 75c976e..b932ff5 100644 --- a/gpxe/src/image/elf.c +++ b/gpxe/src/image/elf.c @@ -21,6 +21,9 @@ * * ELF image format * + * A "pure" ELF image is not a bootable image. There are various + * bootable formats based upon ELF (e.g. Multiboot), which share + * common ELF-related functionality. */ #include @@ -30,23 +33,11 @@ #include #include -struct image_type elf_image_type __image_type ( PROBE_NORMAL ); - typedef Elf32_Ehdr Elf_Ehdr; typedef Elf32_Phdr Elf_Phdr; typedef Elf32_Off Elf_Off; /** - * Execute ELF image - * - * @v image ELF file - * @ret rc Return status code - */ -static int elf_exec ( struct image *image __unused ) { - return -ENOTSUP; -} - -/** * Load ELF segment into memory * * @v image ELF file @@ -112,6 +103,9 @@ int elf_load ( struct image *image ) { unsigned int phnum; int rc; + /* Image type must already have been set by caller */ + assert ( image->type != NULL ); + /* Read ELF header */ copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); if ( memcmp ( &ehdr.e_ident[EI_MAG0], ELFMAG, SELFMAG ) != 0 ) { @@ -119,10 +113,6 @@ int elf_load ( struct image *image ) { return -ENOEXEC; } - /* This is an ELF image, valid or otherwise */ - if ( ! image->type ) - image->type = &elf_image_type; - /* Read ELF program headers */ for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ; phoff += ehdr.e_phentsize, phnum-- ) { @@ -141,10 +131,3 @@ int elf_load ( struct image *image ) { return 0; } - -/** ELF image type */ -struct image_type elf_image_type __image_type ( PROBE_NORMAL ) = { - .name = "ELF", - .load = elf_load, - .exec = elf_exec, -}; diff --git a/gpxe/src/include/gpxe/aoe.h b/gpxe/src/include/gpxe/aoe.h index 8568338..4aab429 100644 --- a/gpxe/src/include/gpxe/aoe.h +++ b/gpxe/src/include/gpxe/aoe.h @@ -11,7 +11,6 @@ #include #include #include -#include #include /** An AoE ATA command */ diff --git a/gpxe/src/include/gpxe/async.h b/gpxe/src/include/gpxe/async.h deleted file mode 100644 index 2d1d31a..0000000 --- a/gpxe/src/include/gpxe/async.h +++ /dev/null @@ -1,228 +0,0 @@ -#ifndef _GPXE_ASYNC_H -#define _GPXE_ASYNC_H - -/** @file - * - * Asynchronous operations - * - */ - -#include - -struct async; - -/** An asynchronous operation ID - * - * Only positive identifiers are valid; negative values are used to - * indicate errors. - */ -typedef long aid_t; - -/** Signals that can be delivered to asynchronous operations */ -enum signal { - /** A child asynchronous operation has completed - * - * The parent should call async_wait() to reap the completed - * child. async_wait() will return the exit status and - * operation identifier of the child. - * - * The handler for this signal can be set to @c NULL; if it - * is, then the children will accumulate as zombies until - * async_wait() is called. - * - * The handler for this signal can also be set to @c SIG_IGN; - * if it is, then the children will automatically be reaped. - * Note that if you use @c SIG_IGN then you will not be able - * to retrieve the return status of the children; the call to - * async_wait() will simply return -ECHILD. - */ - SIGCHLD = 0, - /** Cancel asynchronous operation - * - * This signal should trigger the asynchronous operation to - * cancel itself (including killing all its own children, if - * any), and then call async_done(). The asynchronous - * operation is allowed to not complete immediately. - * - * The handler for this signal can be set to @c NULL; if it - * is, then attempts to cancel the asynchronous operation will - * fail and the operation will complete normally. Anything - * waiting for the operation to cancel will block. - */ - SIGKILL, - /** Update progress of asynchronous operation - * - * This signal should cause the asynchronous operation to - * immediately update the @c completed and @c total fields. - * - * The handler for this signal can be set to @c NULL; if it - * is, then the asynchronous operation is expected to keep its - * @c completed and @c total fields up to date at all times. - */ - SIGUPDATE, - SIGMAX -}; - -/** - * A signal handler - * - * @v async Asynchronous operation - * @v signal Signal received - */ -typedef void ( * signal_handler_t ) ( struct async *async, - enum signal signal ); - -/** Asynchronous operation operations */ -struct async_operations { - /** Reap asynchronous operation - * - * @v async Asynchronous operation - * - * Release all resources associated with the asynchronous - * operation. This will be called only after the asynchronous - * operation itself calls async_done(), so the only remaining - * resources will probably be the memory used by the struct - * async itself. - * - * This method can be set to @c NULL; if it is, then no - * resources will be freed. This may be suitable for - * asynchronous operations that consume no dynamically - * allocated memory. - */ - void ( * reap ) ( struct async *async ); - /** Handle signals */ - signal_handler_t signal[SIGMAX]; -}; - -/** An asynchronous operation */ -struct async { - /** Other asynchronous operations with the same parent */ - struct list_head siblings; - /** Child asynchronous operations */ - struct list_head children; - /** Parent asynchronous operation - * - * This field is optional; if left to NULL then the owner must - * never call async_done(). - */ - struct async *parent; - /** Asynchronous operation ID */ - aid_t aid; - /** Final return status code */ - int rc; - - /** Amount of operation completed so far - * - * The units for this quantity are arbitrary. @c completed - * divded by @total should give something which approximately - * represents the progress through the operation. For a - * download operation, using byte counts would make sense. - * - * This progress indicator should also incorporate the status - * of any child asynchronous operations. - */ - unsigned long completed; - /** Total operation size - * - * See @c completed. A zero value means "total size unknown" - * and is explcitly permitted; users should take this into - * account before calculating @c completed/total. - */ - unsigned long total; - - struct async_operations *aop; -}; - -extern struct async_operations default_async_operations; -extern struct async_operations orphan_async_operations; - -extern aid_t async_init ( struct async *async, struct async_operations *aop, - struct async *parent ); -extern void async_uninit ( struct async *async ); -extern void async_ignore_signal ( struct async *async, enum signal signal ); -extern void async_signal ( struct async *async, enum signal signal ); -extern void async_signal_children ( struct async *async, enum signal signal ); -extern void async_done ( struct async *async, int rc ); -extern aid_t async_wait ( struct async *async, int *rc, int block ); - -/** Default signal handler */ -#define SIG_DFL NULL - -/** Ignore signal */ -#define SIG_IGN async_ignore_signal - -/** - * Initialise orphan asynchronous operation - * - * @v async Asynchronous operation - * @ret aid Asynchronous operation ID - * - * An orphan asynchronous operation can act as a context for child - * operations. However, you must not call async_done() on such an - * operation, since this would attempt to send a signal to its - * (non-existent) parent. Instead, simply free the structure (after - * calling async_wait() to ensure that any child operations have - * completed). - */ -static inline aid_t async_init_orphan ( struct async *async ) { - return async_init ( async, &orphan_async_operations, NULL ); -} - -/** - * Execute and block on an asynchronous operation - * - * @v async_temp Temporary asynchronous operation structure to use - * @v START Code used to start the asynchronous operation - * @ret rc Return status code - * - * This is a notational shorthand for writing - * - * async_init_orphan ( &async_temp ); - * if ( ( rc = START ) == 0 ) - * async_wait ( &async_temp ); - * if ( rc != 0 ) { - * ...handle failure... - * } - * - * and allows you instead to write - * - * if ( ( rc = async_block ( &async_temp, START ) ) != 0 ) { - * ...handle failure... - * } - * - * The argument START is a code snippet; it should initiate an - * asynchronous operation as a child of @c async_temp and return an - * error status code if it failed to do so (e.g. due to malloc() - * failure). - */ -#define async_block( async_temp, START ) ( { \ - int rc; \ - \ - async_init_orphan ( async_temp ); \ - if ( ( rc = START ) == 0 ) \ - async_wait ( async_temp, &rc, 1 ); \ - rc; \ - } ) - -/** - * Execute and block on an asynchronous operation, with progress indicator - * - * @v async_temp Temporary asynchronous operation structure to use - * @v START Code used to start the asynchronous operation - * @ret rc Return status code - * - * As for async_block(), the argument START is a code snippet; it - * should initiate an asynchronous operation as a child of @c - * async_temp and return an error status code if it failed to do so - * (e.g. due to malloc() failure). - */ -#define async_block_progress( async_temp, START ) ( { \ - int rc; \ - \ - async_init_orphan ( async_temp ); \ - if ( ( rc = START ) == 0 ) \ - async_wait_progress ( async_temp, &rc );\ - rc; \ - } ) - -#endif /* _GPXE_ASYNC_H */ diff --git a/gpxe/src/include/gpxe/errfile.h b/gpxe/src/include/gpxe/errfile.h index 42952d7..a059195 100644 --- a/gpxe/src/include/gpxe/errfile.h +++ b/gpxe/src/include/gpxe/errfile.h @@ -132,6 +132,7 @@ #define ERRFILE_infiniband ( ERRFILE_NET | 0x00130000 ) #define ERRFILE_netdev_settings ( ERRFILE_NET | 0x00140000 ) #define ERRFILE_dhcppkt ( ERRFILE_NET | 0x00150000 ) +#define ERRFILE_slam ( ERRFILE_NET | 0x00160000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/gpxe/src/include/gpxe/features.h b/gpxe/src/include/gpxe/features.h index a520131..70daac3 100644 --- a/gpxe/src/include/gpxe/features.h +++ b/gpxe/src/include/gpxe/features.h @@ -41,8 +41,10 @@ #define DHCP_EB_FEATURE_DNS 0x17 /**< DNS protocol */ #define DHCP_EB_FEATURE_BZIMAGE 0x18 /**< bzImage format */ #define DHCP_EB_FEATURE_MULTIBOOT 0x19 /**< Multiboot format */ +#define DHCP_EB_FEATURE_SLAM 0x1a /**< SLAM protocol */ #define DHCP_EB_FEATURE_NBI 0x20 /**< NBI format */ #define DHCP_EB_FEATURE_PXE 0x21 /**< PXE format */ +#define DHCP_EB_FEATURE_ELF 0x22 /**< ELF format */ /** @} */ diff --git a/gpxe/src/include/gpxe/in.h b/gpxe/src/include/gpxe/in.h index 89530a5..40e4d40 100644 --- a/gpxe/src/include/gpxe/in.h +++ b/gpxe/src/include/gpxe/in.h @@ -62,6 +62,15 @@ struct sockaddr_in { uint16_t sin_port; /** IPv4 address */ struct in_addr sin_addr; + /** Padding + * + * This ensures that a struct @c sockaddr_tcpip is large + * enough to hold a socket address for any TCP/IP address + * family. + */ + char pad[ sizeof ( struct sockaddr ) - sizeof ( sa_family_t ) + - sizeof ( uint16_t ) + - sizeof ( struct in_addr ) ]; }; /** diff --git a/gpxe/src/include/gpxe/retry.h b/gpxe/src/include/gpxe/retry.h index e71e7b3..71982fc 100644 --- a/gpxe/src/include/gpxe/retry.h +++ b/gpxe/src/include/gpxe/retry.h @@ -35,10 +35,22 @@ struct retry_timer { }; extern void start_timer ( struct retry_timer *timer ); -extern void start_timer_nodelay ( struct retry_timer *timer ); +extern void start_timer_fixed ( struct retry_timer *timer, + unsigned long timeout ); extern void stop_timer ( struct retry_timer *timer ); /** + * Start timer with no delay + * + * @v timer Retry timer + * + * This starts the timer running with a zero timeout value. + */ +static inline void start_timer_nodelay ( struct retry_timer *timer ) { + start_timer_fixed ( timer, 0 ); +} + +/** * Test to see if timer is currently running * * @v timer Retry timer diff --git a/gpxe/src/net/retry.c b/gpxe/src/net/retry.c index 90b8971..3c93401 100644 --- a/gpxe/src/net/retry.c +++ b/gpxe/src/net/retry.c @@ -74,15 +74,14 @@ void start_timer ( struct retry_timer *timer ) { } /** - * Start timer with no delay + * Start timer with a specified fixed timeout * * @v timer Retry timer - * - * This starts the timer running with a zero timeout value. + * @v timeout Timeout, in ticks */ -void start_timer_nodelay ( struct retry_timer *timer ) { +void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) { start_timer ( timer ); - timer->timeout = 0; + timer->timeout = timeout; } /** diff --git a/gpxe/src/net/udp.c b/gpxe/src/net/udp.c index 89a5b86..8df76a4 100644 --- a/gpxe/src/net/udp.c +++ b/gpxe/src/net/udp.c @@ -29,10 +29,10 @@ struct udp_connection { /** Data transfer interface */ struct xfer_interface xfer; + /** Local socket address */ + struct sockaddr_tcpip local; /** Remote socket address */ struct sockaddr_tcpip peer; - /** Local port on which the connection receives packets */ - unsigned int local_port; }; /** @@ -48,22 +48,22 @@ struct tcpip_protocol udp_protocol; * Bind UDP connection to local port * * @v udp UDP connection - * @v port Local port, in network byte order, or zero * @ret rc Return status code * - * Opens the UDP connection and binds to a local port. If no local - * port is specified, the first available port will be used. + * Opens the UDP connection and binds to the specified local port. If + * no local port is specified, the first available port will be used. */ -static int udp_bind ( struct udp_connection *udp, unsigned int port ) { +static int udp_bind ( struct udp_connection *udp ) { struct udp_connection *existing; static uint16_t try_port = 1024; /* If no port specified, find the first available port */ - if ( ! port ) { + if ( ! udp->local.st_port ) { for ( ; try_port ; try_port++ ) { if ( try_port < 1024 ) continue; - if ( udp_bind ( udp, htons ( try_port ) ) == 0 ) + udp->local.st_port = htons ( try_port ); + if ( udp_bind ( udp ) == 0 ) return 0; } return -EADDRINUSE; @@ -71,16 +71,16 @@ static int udp_bind ( struct udp_connection *udp, unsigned int port ) { /* Attempt bind to local port */ list_for_each_entry ( existing, &udp_conns, list ) { - if ( existing->local_port == port ) { + if ( existing->local.st_port == udp->local.st_port ) { DBGC ( udp, "UDP %p could not bind: port %d in use\n", - udp, ntohs ( port ) ); + udp, ntohs ( udp->local.st_port ) ); return -EADDRINUSE; } } - udp->local_port = port; /* Add to UDP connection list */ - DBGC ( udp, "UDP %p bound to port %d\n", udp, ntohs ( port ) ); + DBGC ( udp, "UDP %p bound to port %d\n", + udp, ntohs ( udp->local.st_port ) ); return 0; } @@ -100,7 +100,6 @@ static int udp_open_common ( struct xfer_interface *xfer, struct sockaddr_tcpip *st_peer = ( struct sockaddr_tcpip * ) peer; struct sockaddr_tcpip *st_local = ( struct sockaddr_tcpip * ) local; struct udp_connection *udp; - unsigned int bind_port; int rc; /* Allocate and initialise structure */ @@ -111,11 +110,12 @@ static int udp_open_common ( struct xfer_interface *xfer, xfer_init ( &udp->xfer, &udp_xfer_operations, &udp->refcnt ); if ( st_peer ) memcpy ( &udp->peer, st_peer, sizeof ( udp->peer ) ); + if ( st_local ) + memcpy ( &udp->local, st_local, sizeof ( udp->local ) ); /* Bind to local port */ if ( ! promisc ) { - bind_port = ( st_local ? st_local->st_port : 0 ); - if ( ( rc = udp_bind ( udp, bind_port ) ) != 0 ) + if ( ( rc = udp_bind ( udp ) ) != 0 ) goto err; } @@ -201,7 +201,7 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf, /* Fill in default values if not explicitly provided */ if ( ! src_port ) - src_port = udp->local_port; + src_port = udp->local.st_port; if ( ! dest ) dest = &udp->peer; @@ -231,17 +231,24 @@ static int udp_tx ( struct udp_connection *udp, struct io_buffer *iobuf, } /** - * Identify UDP connection by local port number + * Identify UDP connection by local address * - * @v local_port Local port (in network-endian order) + * @v local Local address * @ret udp UDP connection, or NULL */ -static struct udp_connection * udp_demux ( unsigned int local_port ) { +static struct udp_connection * udp_demux ( struct sockaddr_tcpip *local ) { + static const struct sockaddr_tcpip empty_sockaddr; struct udp_connection *udp; list_for_each_entry ( udp, &udp_conns, list ) { - if ( ( udp->local_port == local_port ) || - ( udp->local_port == 0 ) ) { + if ( ( ( udp->local.st_family == local->st_family ) || + ( udp->local.st_family == 0 ) ) && + ( ( udp->local.st_port == local->st_port ) || + ( udp->local.st_port == 0 ) ) && + ( ( memcmp ( udp->local.pad, local->pad, + sizeof ( udp->local.pad ) ) == 0 ) || + ( memcmp ( udp->local.pad, empty_sockaddr.pad, + sizeof ( udp->local.pad ) ) == 0 ) ) ) { return udp; } } @@ -300,7 +307,7 @@ static int udp_rx ( struct io_buffer *iobuf, struct sockaddr_tcpip *st_src, /* Parse parameters from header and strip header */ st_src->st_port = udphdr->src; st_dest->st_port = udphdr->dest; - udp = udp_demux ( udphdr->dest ); + udp = udp_demux ( st_dest ); iob_unput ( iobuf, ( iob_len ( iobuf ) - ulen ) ); iob_pull ( iobuf, sizeof ( *udphdr ) ); diff --git a/gpxe/src/net/udp/dhcp.c b/gpxe/src/net/udp/dhcp.c index ecb7378..6ff8afe 100644 --- a/gpxe/src/net/udp/dhcp.c +++ b/gpxe/src/net/udp/dhcp.c @@ -260,6 +260,14 @@ static struct dhcp_settings * dhcpset_create ( const struct dhcphdr *dhcphdr, return dhcpset; } +/** DHCP server address setting */ +struct setting dhcp_server_setting __setting = { + .name = "dhcp-server", + .description = "DHCP server address", + .tag = DHCP_SERVER_IDENTIFIER, + .type = &setting_type_ipv4, +}; + /**************************************************************************** * * DHCP session @@ -924,7 +932,7 @@ static void dhcp_timer_expired ( struct retry_timer *timer, int fail ) { } /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( elapsed > PROXYDHCP_WAIT_TIME ) { + if ( dhcp->dhcpoffer && ( elapsed > PROXYDHCP_WAIT_TIME ) ) { if ( dhcp->state == DHCP_STATE_DISCOVER ) { dhcp_set_state ( dhcp, DHCP_STATE_REQUEST ); return; diff --git a/gpxe/src/net/udp/slam.c b/gpxe/src/net/udp/slam.c new file mode 100644 index 0000000..cb0dfa5 --- /dev/null +++ b/gpxe/src/net/udp/slam.c @@ -0,0 +1,810 @@ +/* + * Copyright (C) 2008 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * Scalable Local Area Multicast protocol + * + * The SLAM protocol is supported only by Etherboot; it was designed + * and implemented by Eric Biederman. A server implementation is + * available in contrib/mini-slamd. There does not appear to be any + * documentation beyond a few sparse comments in Etherboot's + * proto_slam.c. + * + * SLAM packets use three types of data field: + * + * Nul : A single NUL (0) byte, used as a list terminator + * + * Raw : A block of raw data + * + * Int : A variable-length integer, in big-endian order. The length + * of the integer is encoded in the most significant three bits. + * + * Packets received by the client have the following layout: + * + * Int : Transaction identifier. This is an opaque value. + * + * Int : Total number of bytes in the transfer. + * + * Int : Block size, in bytes. + * + * Int : Packet sequence number within the transfer (if this packet + * contains data). + * + * Raw : Packet data (if this packet contains data). + * + * Packets transmitted by the client consist of a run-length-encoded + * representation of the received-blocks bitmap, looking something + * like: + * + * Int : Number of consecutive successfully-received packets + * Int : Number of consecutive missing packets + * Int : Number of consecutive successfully-received packets + * Int : Number of consecutive missing packets + * .... + * Nul + * + */ + +FEATURE ( FEATURE_PROTOCOL, "SLAM", DHCP_EB_FEATURE_SLAM, 1 ); + +/** Default SLAM server port */ +#define SLAM_DEFAULT_PORT 10000 + +/** Default SLAM multicast IP address */ +#define SLAM_DEFAULT_MULTICAST_IP \ + ( ( 239 << 24 ) | ( 255 << 16 ) | ( 1 << 8 ) | ( 1 << 0 ) ) + +/** Default SLAM multicast port */ +#define SLAM_DEFAULT_MULTICAST_PORT 10000 + +/** Maximum SLAM header length */ +#define SLAM_MAX_HEADER_LEN ( 7 /* transaction id */ + 7 /* total_bytes */ + \ + 7 /* block_size */ ) + +/** Maximum number of blocks to request per NACK + * + * This is a policy decision equivalent to selecting a TCP window + * size. + */ +#define SLAM_MAX_BLOCKS_PER_NACK 4 + +/** Maximum SLAM NACK length + * + * We only ever send a NACK for a single range of up to @c + * SLAM_MAX_BLOCKS_PER_NACK blocks. + */ +#define SLAM_MAX_NACK_LEN ( 7 /* block */ + 7 /* #blocks */ + 1 /* NUL */ ) + +/** SLAM slave timeout */ +#define SLAM_SLAVE_TIMEOUT ( 1 * TICKS_PER_SEC ) + +/** A SLAM request */ +struct slam_request { + /** Reference counter */ + struct refcnt refcnt; + + /** Data transfer interface */ + struct xfer_interface xfer; + /** Unicast socket */ + struct xfer_interface socket; + /** Multicast socket */ + struct xfer_interface mc_socket; + + /** Master client retry timer */ + struct retry_timer master_timer; + /** Slave client retry timer */ + struct retry_timer slave_timer; + + /** Cached header */ + uint8_t header[SLAM_MAX_HEADER_LEN]; + /** Size of cached header */ + size_t header_len; + /** Total number of bytes in transfer */ + unsigned long total_bytes; + /** Transfer block size */ + unsigned long block_size; + /** Number of blocks in transfer */ + unsigned long num_blocks; + /** Block bitmap */ + struct bitmap bitmap; + /** NACK sent flag */ + int nack_sent; +}; + +/** + * Free a SLAM request + * + * @v refcnt Reference counter + */ +static void slam_free ( struct refcnt *refcnt ) { + struct slam_request *slam = + container_of ( refcnt, struct slam_request, refcnt ); + + bitmap_free ( &slam->bitmap ); + free ( slam ); +} + +/** + * Mark SLAM request as complete + * + * @v slam SLAM request + * @v rc Return status code + */ +static void slam_finished ( struct slam_request *slam, int rc ) { + static const uint8_t slam_disconnect[] = { 0 }; + + DBGC ( slam, "SLAM %p finished with status code %d (%s)\n", + slam, rc, strerror ( rc ) ); + + /* Send a disconnect message if we ever sent anything to the + * server. + */ + if ( slam->nack_sent ) { + xfer_deliver_raw ( &slam->socket, slam_disconnect, + sizeof ( slam_disconnect ) ); + } + + /* Stop the retry timers */ + stop_timer ( &slam->master_timer ); + stop_timer ( &slam->slave_timer ); + + /* Close all data transfer interfaces */ + xfer_nullify ( &slam->socket ); + xfer_close ( &slam->socket, rc ); + xfer_nullify ( &slam->mc_socket ); + xfer_close ( &slam->mc_socket, rc ); + xfer_nullify ( &slam->xfer ); + xfer_close ( &slam->xfer, rc ); +} + +/**************************************************************************** + * + * TX datapath + * + */ + +/** + * Add a variable-length value to a SLAM packet + * + * @v slam SLAM request + * @v iobuf I/O buffer + * @v value Value to add + * @ret rc Return status code + * + * Adds a variable-length value to the end of an I/O buffer. Will + * always leave at least one byte of tailroom in the I/O buffer (to + * allow space for the terminating NUL). + */ +static int slam_put_value ( struct slam_request *slam, + struct io_buffer *iobuf, unsigned long value ) { + uint8_t *data; + size_t len; + unsigned int i; + + /* Calculate variable length required to store value. Always + * leave at least one byte in the I/O buffer. + */ + len = ( ( flsl ( value ) + 10 ) / 8 ); + if ( len >= iob_tailroom ( iobuf ) ) { + DBGC2 ( slam, "SLAM %p cannot add %d-byte value\n", + slam, len ); + return -ENOBUFS; + } + /* There is no valid way within the protocol that we can end + * up trying to push a full-sized long (i.e. without space for + * the length encoding). + */ + assert ( len <= sizeof ( value ) ); + + /* Add value */ + data = iob_put ( iobuf, len ); + for ( i = len ; i-- ; ) { + data[i] = value; + value >>= 8; + } + *data |= ( len << 5 ); + assert ( value == 0 ); + + return 0; +} + +/** + * Send SLAM NACK packet + * + * @v slam SLAM request + * @ret rc Return status code + */ +static int slam_tx_nack ( struct slam_request *slam ) { + struct io_buffer *iobuf; + unsigned long first_block; + unsigned long num_blocks; + uint8_t *nul; + int rc; + + /* Mark NACK as sent, so that we know we have to disconnect later */ + slam->nack_sent = 1; + + /* Allocate I/O buffer */ + iobuf = xfer_alloc_iob ( &slam->socket, SLAM_MAX_NACK_LEN ); + if ( ! iobuf ) { + DBGC ( slam, "SLAM %p could not allocate I/O buffer\n", + slam ); + return -ENOMEM; + } + + /* Construct NACK. We always request only a single packet; + * this allows us to force multicast-TFTP-style flow control + * on the SLAM server, which will otherwise just blast the + * data out as fast as it can. On a gigabit network, without + * RX checksumming, this would inevitably cause packet drops. + */ + first_block = bitmap_first_gap ( &slam->bitmap ); + for ( num_blocks = 1 ; ; num_blocks++ ) { + if ( num_blocks >= SLAM_MAX_BLOCKS_PER_NACK ) + break; + if ( ( first_block + num_blocks ) >= slam->num_blocks ) + break; + if ( bitmap_test ( &slam->bitmap, + ( first_block + num_blocks ) ) ) + break; + } + if ( first_block ) { + DBGCP ( slam, "SLAM %p transmitting NACK for blocks " + "%ld-%ld\n", slam, first_block, + ( first_block + num_blocks - 1 ) ); + } else { + DBGC ( slam, "SLAM %p transmitting initial NACK for blocks " + "0-%ld\n", slam, ( num_blocks - 1 ) ); + } + if ( ( rc = slam_put_value ( slam, iobuf, first_block ) ) != 0 ) + return rc; + if ( ( rc = slam_put_value ( slam, iobuf, num_blocks ) ) != 0 ) + return rc; + nul = iob_put ( iobuf, 1 ); + *nul = 0; + + /* Transmit packet */ + return xfer_deliver_iob ( &slam->socket, iobuf ); +} + +/** + * Handle SLAM master client retry timer expiry + * + * @v timer Master retry timer + * @v fail Failure indicator + */ +static void slam_master_timer_expired ( struct retry_timer *timer, + int fail ) { + struct slam_request *slam = + container_of ( timer, struct slam_request, master_timer ); + + if ( fail ) { + /* Allow timer to stop running. We will terminate the + * connection only if the slave timer times out. + */ + DBGC ( slam, "SLAM %p giving up acting as master client\n", + slam ); + } else { + /* Retransmit NACK */ + start_timer ( timer ); + slam_tx_nack ( slam ); + } +} + +/** + * Handle SLAM slave client retry timer expiry + * + * @v timer Master retry timer + * @v fail Failure indicator + */ +static void slam_slave_timer_expired ( struct retry_timer *timer, + int fail ) { + struct slam_request *slam = + container_of ( timer, struct slam_request, slave_timer ); + + if ( fail ) { + /* Terminate connection */ + slam_finished ( slam, -ETIMEDOUT ); + } else { + /* Try sending a NACK */ + DBGC ( slam, "SLAM %p trying to become master client\n", + slam ); + start_timer ( timer ); + slam_tx_nack ( slam ); + } +} + +/**************************************************************************** + * + * RX datapath + * + */ + +/** + * Read and strip a variable-length value from a SLAM packet + * + * @v slam SLAM request + * @v iobuf I/O buffer + * @v value Value to fill in, or NULL to ignore value + * @ret rc Return status code + * + * Reads a variable-length value from the start of the I/O buffer. + */ +static int slam_pull_value ( struct slam_request *slam, + struct io_buffer *iobuf, + unsigned long *value ) { + uint8_t *data; + size_t len; + + /* Sanity check */ + if ( iob_len ( iobuf ) == 0 ) { + DBGC ( slam, "SLAM %p empty value\n", slam ); + return -EINVAL; + } + + /* Read and verify length of value */ + data = iobuf->data; + len = ( *data >> 5 ); + if ( ( len == 0 ) || + ( value && ( len > sizeof ( *value ) ) ) ) { + DBGC ( slam, "SLAM %p invalid value length %d bytes\n", + slam, len ); + return -EINVAL; + } + if ( len > iob_len ( iobuf ) ) { + DBGC ( slam, "SLAM %p value extends beyond I/O buffer\n", + slam ); + return -EINVAL; + } + + /* Read value */ + iob_pull ( iobuf, len ); + *value = ( *data & 0x1f ); + while ( --len ) { + *value <<= 8; + *value |= *(++data); + } + + return 0; +} + +/** + * Read and strip SLAM header + * + * @v slam SLAM request + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int slam_pull_header ( struct slam_request *slam, + struct io_buffer *iobuf ) { + void *header = iobuf->data; + int rc; + + /* If header matches cached header, just pull it and return */ + if ( ( slam->header_len <= iob_len ( iobuf ) ) && + ( memcmp ( slam->header, iobuf->data, slam->header_len ) == 0 )){ + iob_pull ( iobuf, slam->header_len ); + return 0; + } + + DBGC ( slam, "SLAM %p detected changed header; resetting\n", slam ); + + /* Read and strip transaction ID, total number of bytes, and + * block size. + */ + if ( ( rc = slam_pull_value ( slam, iobuf, NULL ) ) != 0 ) + return rc; + if ( ( rc = slam_pull_value ( slam, iobuf, + &slam->total_bytes ) ) != 0 ) + return rc; + if ( ( rc = slam_pull_value ( slam, iobuf, + &slam->block_size ) ) != 0 ) + return rc; + + /* Update the cached header */ + slam->header_len = ( iobuf->data - header ); + assert ( slam->header_len <= sizeof ( slam->header ) ); + memcpy ( slam->header, header, slam->header_len ); + + /* Calculate number of blocks */ + slam->num_blocks = ( ( slam->total_bytes + slam->block_size - 1 ) / + slam->block_size ); + + DBGC ( slam, "SLAM %p has total bytes %ld, block size %ld, num " + "blocks %ld\n", slam, slam->total_bytes, slam->block_size, + slam->num_blocks ); + + /* Discard and reset the bitmap */ + bitmap_free ( &slam->bitmap ); + memset ( &slam->bitmap, 0, sizeof ( slam->bitmap ) ); + + /* Allocate a new bitmap */ + if ( ( rc = bitmap_resize ( &slam->bitmap, + slam->num_blocks ) ) != 0 ) { + /* Failure to allocate a bitmap is fatal */ + DBGC ( slam, "SLAM %p could not allocate bitmap for %ld " + "blocks: %s\n", slam, slam->num_blocks, + strerror ( rc ) ); + slam_finished ( slam, rc ); + return rc; + } + + /* Notify recipient of file size */ + xfer_seek ( &slam->xfer, slam->total_bytes, SEEK_SET ); + + return 0; +} + +/** + * Receive SLAM data packet + * + * @v mc_socket SLAM multicast socket + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int slam_mc_socket_deliver ( struct xfer_interface *mc_socket, + struct io_buffer *iobuf, + struct xfer_metadata *rx_meta __unused ) { + struct slam_request *slam = + container_of ( mc_socket, struct slam_request, mc_socket ); + struct xfer_metadata meta; + unsigned long packet; + size_t len; + int rc; + + /* Stop the master client timer. Restart the slave client timer. */ + stop_timer ( &slam->master_timer ); + stop_timer ( &slam->slave_timer ); + start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT ); + + /* Read and strip packet header */ + if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 ) + goto err_discard; + + /* Read and strip packet number */ + if ( ( rc = slam_pull_value ( slam, iobuf, &packet ) ) != 0 ) + goto err_discard; + + /* Sanity check packet number */ + if ( packet >= slam->num_blocks ) { + DBGC ( slam, "SLAM %p received out-of-range packet %ld " + "(num_blocks=%ld)\n", slam, packet, slam->num_blocks ); + rc = -EINVAL; + goto err_discard; + } + + /* Sanity check length */ + len = iob_len ( iobuf ); + if ( len > slam->block_size ) { + DBGC ( slam, "SLAM %p received oversize packet of %zd bytes " + "(block_size=%ld)\n", slam, len, slam->block_size ); + rc = -EINVAL; + goto err_discard; + } + if ( ( packet != ( slam->num_blocks - 1 ) ) && + ( len < slam->block_size ) ) { + DBGC ( slam, "SLAM %p received short packet of %zd bytes " + "(block_size=%ld)\n", slam, len, slam->block_size ); + rc = -EINVAL; + goto err_discard; + } + + /* If we have already seen this packet, discard it */ + if ( bitmap_test ( &slam->bitmap, packet ) ) { + goto discard; + } + + /* Pass to recipient */ + memset ( &meta, 0, sizeof ( meta ) ); + meta.whence = SEEK_SET; + meta.offset = ( packet * slam->block_size ); + if ( ( rc = xfer_deliver_iob_meta ( &slam->xfer, iobuf, + &meta ) ) != 0 ) + goto err; + + /* Mark block as received */ + bitmap_set ( &slam->bitmap, packet ); + + /* If we have received all blocks, terminate */ + if ( bitmap_full ( &slam->bitmap ) ) + slam_finished ( slam, 0 ); + + return 0; + + err_discard: + discard: + free_iob ( iobuf ); + err: + return rc; +} + +/** + * Receive SLAM non-data packet + * + * @v socket SLAM unicast socket + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int slam_socket_deliver ( struct xfer_interface *socket, + struct io_buffer *iobuf, + struct xfer_metadata *rx_meta __unused ) { + struct slam_request *slam = + container_of ( socket, struct slam_request, socket ); + int rc; + + /* Restart the master client timer */ + stop_timer ( &slam->master_timer ); + start_timer ( &slam->master_timer ); + + /* Read and strip packet header */ + if ( ( rc = slam_pull_header ( slam, iobuf ) ) != 0 ) + goto discard; + + /* Sanity check */ + if ( iob_len ( iobuf ) != 0 ) { + DBGC ( slam, "SLAM %p received trailing garbage:\n", slam ); + DBGC_HD ( slam, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto discard; + } + + /* Discard packet */ + free_iob ( iobuf ); + + /* Send NACK in reply */ + slam_tx_nack ( slam ); + + return 0; + + discard: + free_iob ( iobuf ); + return rc; + +} + +/** + * Close SLAM unicast socket + * + * @v socket SLAM unicast socket + * @v rc Reason for close + */ +static void slam_socket_close ( struct xfer_interface *socket, int rc ) { + struct slam_request *slam = + container_of ( socket, struct slam_request, socket ); + + DBGC ( slam, "SLAM %p unicast socket closed: %s\n", + slam, strerror ( rc ) ); + + slam_finished ( slam, rc ); +} + +/** SLAM unicast socket data transfer operations */ +static struct xfer_interface_operations slam_socket_operations = { + .close = slam_socket_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = slam_socket_deliver, + .deliver_raw = xfer_deliver_as_iob, +}; + +/** + * Close SLAM multicast socket + * + * @v mc_socket SLAM multicast socket + * @v rc Reason for close + */ +static void slam_mc_socket_close ( struct xfer_interface *mc_socket, int rc ){ + struct slam_request *slam = + container_of ( mc_socket, struct slam_request, mc_socket ); + + DBGC ( slam, "SLAM %p multicast socket closed: %s\n", + slam, strerror ( rc ) ); + + slam_finished ( slam, rc ); +} + +/** SLAM multicast socket data transfer operations */ +static struct xfer_interface_operations slam_mc_socket_operations = { + .close = slam_mc_socket_close, + .vredirect = xfer_vopen, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = slam_mc_socket_deliver, + .deliver_raw = xfer_deliver_as_iob, +}; + +/**************************************************************************** + * + * Data transfer interface + * + */ + +/** + * Close SLAM data transfer interface + * + * @v xfer SLAM data transfer interface + * @v rc Reason for close + */ +static void slam_xfer_close ( struct xfer_interface *xfer, int rc ) { + struct slam_request *slam = + container_of ( xfer, struct slam_request, xfer ); + + DBGC ( slam, "SLAM %p data transfer interface closed: %s\n", + slam, strerror ( rc ) ); + + slam_finished ( slam, rc ); +} + +/** SLAM data transfer operations */ +static struct xfer_interface_operations slam_xfer_operations = { + .close = slam_xfer_close, + .vredirect = ignore_xfer_vredirect, + .window = unlimited_xfer_window, + .alloc_iob = default_xfer_alloc_iob, + .deliver_iob = xfer_deliver_as_raw, + .deliver_raw = ignore_xfer_deliver_raw, +}; + +/** + * Parse SLAM URI multicast address + * + * @v slam SLAM request + * @v path Path portion of x-slam:// URI + * @v address Socket address to fill in + * @ret rc Return status code + */ +static int slam_parse_multicast_address ( struct slam_request *slam, + const char *path, + struct sockaddr_in *address ) { + char path_dup[ strlen ( path ) /* no +1 */ ]; + char *sep; + char *end; + + /* Create temporary copy of path, minus the leading '/' */ + assert ( *path == '/' ); + memcpy ( path_dup, ( path + 1 ) , sizeof ( path_dup ) ); + + /* Parse port, if present */ + sep = strchr ( path_dup, ':' ); + if ( sep ) { + *(sep++) = '\0'; + address->sin_port = htons ( strtoul ( sep, &end, 0 ) ); + if ( *end != '\0' ) { + DBGC ( slam, "SLAM %p invalid multicast port " + "\"%s\"\n", slam, sep ); + return -EINVAL; + } + } + + /* Parse address */ + if ( inet_aton ( path_dup, &address->sin_addr ) == 0 ) { + DBGC ( slam, "SLAM %p invalid multicast address \"%s\"\n", + slam, path_dup ); + return -EINVAL; + } + + return 0; +} + +/** + * Initiate a SLAM request + * + * @v xfer Data transfer interface + * @v uri Uniform Resource Identifier + * @ret rc Return status code + */ +static int slam_open ( struct xfer_interface *xfer, struct uri *uri ) { + static const struct sockaddr_in default_multicast = { + .sin_family = AF_INET, + .sin_port = htons ( SLAM_DEFAULT_MULTICAST_PORT ), + .sin_addr = { htonl ( SLAM_DEFAULT_MULTICAST_IP ) }, + }; + struct slam_request *slam; + struct sockaddr_tcpip server; + struct sockaddr_in multicast; + int rc; + + /* Sanity checks */ + if ( ! uri->host ) + return -EINVAL; + + /* Allocate and populate structure */ + slam = zalloc ( sizeof ( *slam ) ); + if ( ! slam ) + return -ENOMEM; + slam->refcnt.free = slam_free; + xfer_init ( &slam->xfer, &slam_xfer_operations, &slam->refcnt ); + xfer_init ( &slam->socket, &slam_socket_operations, &slam->refcnt ); + xfer_init ( &slam->mc_socket, &slam_mc_socket_operations, + &slam->refcnt ); + slam->master_timer.expired = slam_master_timer_expired; + slam->slave_timer.expired = slam_slave_timer_expired; + /* Fake an invalid cached header of { 0x00, ... } */ + slam->header_len = 1; + /* Fake parameters for initial NACK */ + slam->num_blocks = 1; + if ( ( rc = bitmap_resize ( &slam->bitmap, 1 ) ) != 0 ) { + DBGC ( slam, "SLAM %p could not allocate initial bitmap: " + "%s\n", slam, strerror ( rc ) ); + goto err; + } + + /* Open unicast socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st_port = htons ( uri_port ( uri, SLAM_DEFAULT_PORT ) ); + if ( ( rc = xfer_open_named_socket ( &slam->socket, SOCK_DGRAM, + ( struct sockaddr * ) &server, + uri->host, NULL ) ) != 0 ) { + DBGC ( slam, "SLAM %p could not open unicast socket: %s\n", + slam, strerror ( rc ) ); + goto err; + } + + /* Open multicast socket */ + memcpy ( &multicast, &default_multicast, sizeof ( multicast ) ); + if ( uri->path && + ( ( rc = slam_parse_multicast_address ( slam, uri->path, + &multicast ) ) != 0 ) ) { + goto err; + } + if ( ( rc = xfer_open_socket ( &slam->mc_socket, SOCK_DGRAM, + ( struct sockaddr * ) &multicast, + ( struct sockaddr * ) &multicast ) ) != 0 ) { + DBGC ( slam, "SLAM %p could not open multicast socket: %s\n", + slam, strerror ( rc ) ); + goto err; + } + + /* Start slave retry timer */ + start_timer_fixed ( &slam->slave_timer, SLAM_SLAVE_TIMEOUT ); + + /* Attach to parent interface, mortalise self, and return */ + xfer_plug_plug ( &slam->xfer, xfer ); + ref_put ( &slam->refcnt ); + return 0; + + err: + slam_finished ( slam, rc ); + ref_put ( &slam->refcnt ); + return rc; +} + +/** SLAM URI opener */ +struct uri_opener slam_uri_opener __uri_opener = { + .scheme = "x-slam", + .open = slam_open, +}; diff --git a/gpxe/src/proto/slam.c b/gpxe/src/proto/slam.c deleted file mode 100644 index a25c30d..0000000 --- a/gpxe/src/proto/slam.c +++ /dev/null @@ -1,541 +0,0 @@ -#if 0 - -/* - * IMPORTANT - * - * This file should be rewritten to avoid the use of a bitmap. Our - * buffer routines can cope with being handed blocks in an arbitrary - * order, duplicate blocks, etc. This code could be substantially - * simplified by taking advantage of these features. - * - */ - -#define SLAM_PORT 10000 -#define SLAM_MULTICAST_IP ((239<<24)|(255<<16)|(1<<8)|(1<<0)) -#define SLAM_MULTICAST_PORT 10000 -#define SLAM_LOCAL_PORT 10000 - -/* Set the timeout intervals to at least 1 second so - * on a 100Mbit ethernet can receive 10000 packets - * in one second. - * - * The only case that is likely to trigger all of the nodes - * firing a nack packet is a slow server. The odds of this - * happening could be reduced being slightly smarter and utilizing - * the multicast channels for nacks. But that only improves the odds - * it doesn't improve the worst case. So unless this proves to be - * a common case having the control data going unicast should increase - * the odds of the data not being dropped. - * - * When doing exponential backoff we increase just the timeout - * interval and not the base to optimize for throughput. This is only - * expected to happen when the server is down. So having some nodes - * pinging immediately should get the transmission restarted quickly after a - * server restart. The host nic won't be to baddly swamped because of - * the random distribution of the nodes. - * - */ -#define SLAM_INITIAL_MIN_TIMEOUT (TICKS_PER_SEC/3) -#define SLAM_INITIAL_TIMEOUT_INTERVAL (TICKS_PER_SEC) -#define SLAM_BASE_MIN_TIMEOUT (2*TICKS_PER_SEC) -#define SLAM_BASE_TIMEOUT_INTERVAL (4*TICKS_PER_SEC) -#define SLAM_BACKOFF_LIMIT 5 -#define SLAM_MAX_RETRIES 20 - -/*** Packets Formats *** - * Data Packet: - * transaction - * total bytes - * block size - * packet # - * data - * - * Status Request Packet - * transaction - * total bytes - * block size - * - * Status Packet - * received packets - * requested packets - * received packets - * requested packets - * ... - * received packets - * requested packtes - * 0 - */ - -#define MAX_HDR (7 + 7 + 7) /* transaction, total size, block size */ -#define MIN_HDR (1 + 1 + 1) /* transactino, total size, block size */ - -#define MAX_SLAM_REQUEST MAX_HDR -#define MIN_SLAM_REQUEST MIN_HDR - -#define MIN_SLAM_DATA (MIN_HDR + 1) - -static struct slam_nack { - struct iphdr ip; - struct udphdr udp; - unsigned char data[ETH_MAX_MTU - - (sizeof(struct iphdr) + sizeof(struct udphdr))]; -} nack; - -struct slam_state { - unsigned char hdr[MAX_HDR]; - unsigned long hdr_len; - unsigned long block_size; - unsigned long total_bytes; - unsigned long total_packets; - - unsigned long received_packets; - - struct buffer *buffer; - unsigned char *image; - unsigned char *bitmap; -} state; - - -static void init_slam_state(void) -{ - state.hdr_len = sizeof(state.hdr); - memset(state.hdr, 0, state.hdr_len); - state.block_size = 0; - state.total_packets = 0; - - state.received_packets = 0; - - state.image = 0; - state.bitmap = 0; -} - -struct slam_info { - struct sockaddr_in server; - struct sockaddr_in local; - struct sockaddr_in multicast; - int sent_nack; - struct buffer *buffer; -}; - -#define SLAM_TIMEOUT 0 -#define SLAM_REQUEST 1 -#define SLAM_DATA 2 -static int await_slam(int ival __unused, void *ptr, - unsigned short ptype __unused, struct iphdr *ip, - struct udphdr *udp, struct tcphdr *tcp __unused) -{ - struct slam_info *info = ptr; - if (!udp) { - return 0; - } - /* I can receive two kinds of packets here, a multicast data packet, - * or a unicast request for information - */ - /* Check for a data request packet */ - if ((ip->dest.s_addr == arptable[ARP_CLIENT].ipaddr.s_addr) && - (ntohs(udp->dest) == info->local.sin_port) && - (nic.packetlen >= - ETH_HLEN + - sizeof(struct iphdr) + - sizeof(struct udphdr) + - MIN_SLAM_REQUEST)) { - return SLAM_REQUEST; - } - /* Check for a multicast data packet */ - if ((ip->dest.s_addr == info->multicast.sin_addr.s_addr) && - (ntohs(udp->dest) == info->multicast.sin_port) && - (nic.packetlen >= - ETH_HLEN + - sizeof(struct iphdr) + - sizeof(struct udphdr) + - MIN_SLAM_DATA)) { - return SLAM_DATA; - } -#if 0 - printf("#"); - printf("dest: %@ port: %d len: %d\n", - ip->dest.s_addr, ntohs(udp->dest), nic.packetlen); -#endif - return 0; - -} - -static int slam_encode( - unsigned char **ptr, unsigned char *end, unsigned long value) -{ - unsigned char *data = *ptr; - int bytes; - bytes = sizeof(value); - while ((bytes > 0) && ((0xff & (value >> ((bytes -1)<<3))) == 0)) { - bytes--; - } - if (bytes <= 0) { - bytes = 1; - } - if (data + bytes >= end) { - return -1; - } - if ((0xe0 & (value >> ((bytes -1)<<3))) == 0) { - /* packed together */ - *data = (bytes << 5) | (value >> ((bytes -1)<<3)); - } else { - bytes++; - *data = (bytes << 5); - } - bytes--; - data++; - while(bytes) { - *(data++) = 0xff & (value >> ((bytes -1)<<3)); - bytes--; - } - *ptr = data; - return 0; -} - -static int slam_skip(unsigned char **ptr, unsigned char *end) -{ - int bytes; - if (*ptr >= end) { - return -1; - } - bytes = ((**ptr) >> 5) & 7; - if (bytes == 0) { - return -1; - } - if (*ptr + bytes >= end) { - return -1; - } - (*ptr) += bytes; - return 0; - -} - -static unsigned long slam_decode(unsigned char **ptr, unsigned char *end, - int *err) -{ - unsigned long value; - unsigned bytes; - if (*ptr >= end) { - *err = -1; - } - bytes = ((**ptr) >> 5) & 7; - if ((bytes == 0) || (bytes > sizeof(unsigned long))) { - *err = -1; - return 0; - } - if ((*ptr) + bytes >= end) { - *err = -1; - } - value = (**ptr) & 0x1f; - bytes--; - (*ptr)++; - while(bytes) { - value <<= 8; - value |= **ptr; - (*ptr)++; - bytes--; - } - return value; -} - - -static long slam_sleep_interval(int exp) -{ - long range; - long divisor; - long interval; - range = SLAM_BASE_TIMEOUT_INTERVAL; - if (exp < 0) { - divisor = RAND_MAX/SLAM_INITIAL_TIMEOUT_INTERVAL; - } else { - if (exp > SLAM_BACKOFF_LIMIT) - exp = SLAM_BACKOFF_LIMIT; - divisor = RAND_MAX/(range << exp); - } - interval = random()/divisor; - if (exp < 0) { - interval += SLAM_INITIAL_MIN_TIMEOUT; - } else { - interval += SLAM_BASE_MIN_TIMEOUT; - } - return interval; -} - - -static unsigned char *reinit_slam_state( - unsigned char *header, unsigned char *end) -{ - unsigned long total_bytes; - unsigned long block_size; - - unsigned long bitmap_len; - unsigned long max_packet_len; - unsigned char *data; - int err; - -#if 0 - printf("reinit\n"); -#endif - data = header; - - state.hdr_len = 0; - err = slam_skip(&data, end); /* transaction id */ - total_bytes = slam_decode(&data, end, &err); - block_size = slam_decode(&data, end, &err); - if (err) { - printf("ALERT: slam size out of range\n"); - return 0; - } - state.block_size = block_size; - state.total_bytes = total_bytes; - state.total_packets = (total_bytes + block_size - 1)/block_size; - state.hdr_len = data - header; - state.received_packets = 0; - - data = state.hdr; - slam_encode(&data, &state.hdr[sizeof(state.hdr)], state.total_packets); - max_packet_len = data - state.hdr; - memcpy(state.hdr, header, state.hdr_len); - -#if 0 - printf("block_size: %ld\n", block_size); - printf("total_bytes: %ld\n", total_bytes); - printf("total_packets: %ld\n", state.total_packets); - printf("hdr_len: %ld\n", state.hdr_len); - printf("max_packet_len: %ld\n", max_packet_len); -#endif - - if (state.block_size > ETH_MAX_MTU - ( - sizeof(struct iphdr) + sizeof(struct udphdr) + - state.hdr_len + max_packet_len)) { - printf("ALERT: slam blocksize to large\n"); - return 0; - } - bitmap_len = (state.total_packets + 1 + 7)/8; - state.image = phys_to_virt ( state.buffer->addr ); - /* We don't use the buffer routines properly yet; fake it */ - state.buffer->fill = total_bytes; - state.bitmap = state.image + total_bytes; - if ((unsigned long)state.image < 1024*1024) { - printf("ALERT: slam filesize to large for available memory\n"); - return 0; - } - memset(state.bitmap, 0, bitmap_len); - - return header + state.hdr_len; -} - -static int slam_recv_data(unsigned char *data) -{ - unsigned long packet; - unsigned long data_len; - int err; - struct udphdr *udp; - udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)]; - err = 0; - packet = slam_decode(&data, &nic.packet[nic.packetlen], &err); - if (err || (packet > state.total_packets)) { - printf("ALERT: Invalid packet number\n"); - return 0; - } - /* Compute the expected data length */ - if (packet != state.total_packets -1) { - data_len = state.block_size; - } else { - data_len = state.total_bytes % state.block_size; - } - /* If the packet size is wrong drop the packet and then continue */ - if (ntohs(udp->len) != (data_len + (data - (unsigned char*)udp))) { - printf("ALERT: udp packet is not the correct size\n"); - return 1; - } - if (nic.packetlen < data_len + (data - nic.packet)) { - printf("ALERT: Ethernet packet shorter than data_len\n"); - return 1; - } - if (data_len > state.block_size) { - data_len = state.block_size; - } - if (((state.bitmap[packet >> 3] >> (packet & 7)) & 1) == 0) { - /* Non duplicate packet */ - state.bitmap[packet >> 3] |= (1 << (packet & 7)); - memcpy(state.image + (packet*state.block_size), data, data_len); - state.received_packets++; - } else { -#ifdef MDEBUG - printf("\n"); -#endif - } - return 1; -} - -static void transmit_nack(unsigned char *ptr, struct slam_info *info) -{ - int nack_len; - /* Ensure the packet is null terminated */ - *ptr++ = 0; - nack_len = ptr - (unsigned char *)&nack; - build_udp_hdr(info->server.sin_addr.s_addr, info->local.sin_port, - info->server.sin_port, 1, nack_len, &nack); - ip_transmit(nack_len, &nack); -#if defined(MDEBUG) && 0 - printf("Sent NACK to %@ bytes: %d have:%ld/%ld\n", - info->server_ip, nack_len, - state.received_packets, state.total_packets); -#endif -} - -static void slam_send_nack(struct slam_info *info) -{ - unsigned char *ptr, *end; - /* Either I timed out or I was explicitly - * asked for a request packet - */ - ptr = &nack.data[0]; - /* Reserve space for the trailling null */ - end = &nack.data[sizeof(nack.data) -1]; - if (!state.bitmap) { - slam_encode(&ptr, end, 0); - slam_encode(&ptr, end, 1); - } - else { - /* Walk the bitmap */ - unsigned long i; - unsigned long len; - unsigned long max; - int value; - int last; - /* Compute the last bit and store an inverted trailer */ - max = state.total_packets; - value = ((state.bitmap[(max -1) >> 3] >> ((max -1) & 7) ) & 1); - value = !value; - state.bitmap[max >> 3] &= ~(1 << (max & 7)); - state.bitmap[max >> 3] |= value << (max & 7); - - len = 0; - last = 1; /* Start with the received packets */ - for(i = 0; i <= max; i++) { - value = (state.bitmap[i>>3] >> (i & 7)) & 1; - if (value == last) { - len++; - } else { - if (slam_encode(&ptr, end, len)) - break; - last = value; - len = 1; - } - } - } - info->sent_nack = 1; - transmit_nack(ptr, info); -} - -static void slam_send_disconnect(struct slam_info *info) -{ - if (info->sent_nack) { - /* A disconnect is a packet with just the null terminator */ - transmit_nack(&nack.data[0], info); - } - info->sent_nack = 0; -} - - -static int proto_slam(struct slam_info *info) -{ - int retry; - long timeout; - - init_slam_state(); - state.buffer = info->buffer; - - retry = -1; - rx_qdrain(); - /* Arp for my server */ - if (arptable[ARP_SERVER].ipaddr.s_addr != info->server.sin_addr.s_addr) { - arptable[ARP_SERVER].ipaddr.s_addr = info->server.sin_addr.s_addr; - memset(arptable[ARP_SERVER].node, 0, ETH_ALEN); - } - /* If I'm running over multicast join the multicast group */ - join_group(IGMP_SERVER, info->multicast.sin_addr.s_addr); - for(;;) { - unsigned char *header; - unsigned char *data; - int type; - header = data = 0; - - timeout = slam_sleep_interval(retry); - type = await_reply(await_slam, 0, info, timeout); - /* Compute the timeout for next time */ - if (type == SLAM_TIMEOUT) { - /* If I timeouted recompute the next timeout */ - if (retry++ > SLAM_MAX_RETRIES) { - return 0; - } - } else { - retry = 0; - } - if ((type == SLAM_DATA) || (type == SLAM_REQUEST)) { - /* Check the incomming packet and reinit the data - * structures if necessary. - */ - header = &nic.packet[ETH_HLEN + - sizeof(struct iphdr) + sizeof(struct udphdr)]; - data = header + state.hdr_len; - if (memcmp(state.hdr, header, state.hdr_len) != 0) { - /* Something is fishy reset the transaction */ - data = reinit_slam_state(header, &nic.packet[nic.packetlen]); - if (!data) { - return 0; - } - } - } - if (type == SLAM_DATA) { - if (!slam_recv_data(data)) { - return 0; - } - if (state.received_packets == state.total_packets) { - /* We are done get out */ - break; - } - } - if ((type == SLAM_TIMEOUT) || (type == SLAM_REQUEST)) { - /* Either I timed out or I was explicitly - * asked by a request packet - */ - slam_send_nack(info); - } - } - slam_send_disconnect(info); - - /* Leave the multicast group */ - leave_group(IGMP_SERVER); - /* FIXME don't overwrite myself */ - /* load file to correct location */ - return 1; -} - -static int url_slam ( char *url __unused, struct sockaddr_in *server, - char *file, struct buffer *buffer ) { - struct slam_info info; - /* Set the defaults */ - info.server = *server; - info.multicast.sin_addr.s_addr = htonl(SLAM_MULTICAST_IP); - info.multicast.sin_port = SLAM_MULTICAST_PORT; - info.local.sin_addr.s_addr = arptable[ARP_CLIENT].ipaddr.s_addr; - info.local.sin_port = SLAM_LOCAL_PORT; - info.buffer = buffer; - info.sent_nack = 0; - if (file[0]) { - printf("\nBad url\n"); - return 0; - } - return proto_slam(&info); -} - -struct protocol slam_protocol __protocol = { - .name = "x-slam", - .default_port = SLAM_PORT, - .load = url_slam, -}; - -#endif -- 2.7.4