123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599 |
- /*
- * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- #include <string.h>
- #include <errno.h>
- #include <ipxe/refcnt.h>
- #include <ipxe/list.h>
- #include <ipxe/netdevice.h>
- #include <ipxe/fakedhcp.h>
- #include <ipxe/process.h>
- #include <ipxe/uri.h>
- #include <ipxe/in.h>
- #include <ipxe/socket.h>
- #include <ipxe/tcpip.h>
- #include <ipxe/xferbuf.h>
- #include <ipxe/open.h>
- #include <ipxe/dhcppkt.h>
- #include <ipxe/udp.h>
- #include <ipxe/efi/efi.h>
- #include <ipxe/efi/efi_snp.h>
- #include <ipxe/efi/efi_pxe.h>
- #include <ipxe/efi/Protocol/PxeBaseCode.h>
- #include <usr/ifmgmt.h>
- #include <config/general.h>
-
- /** @file
- *
- * EFI PXE base code protocol
- *
- */
-
- /* Downgrade user experience if configured to do so
- *
- * See comments in efi_snp.c
- */
- #ifdef EFI_DOWNGRADE_UX
- static EFI_GUID dummy_pxe_base_code_protocol_guid = {
- 0x70647523, 0x2320, 0x7477,
- { 0x66, 0x20, 0x23, 0x6d, 0x6f, 0x72, 0x6f, 0x6e }
- };
- #define efi_pxe_base_code_protocol_guid dummy_pxe_base_code_protocol_guid
- #endif
-
- /** A PXE base code */
- struct efi_pxe {
- /** Reference count */
- struct refcnt refcnt;
- /** Underlying network device */
- struct net_device *netdev;
- /** Name */
- const char *name;
- /** List of PXE base codes */
- struct list_head list;
-
- /** Installed handle */
- EFI_HANDLE handle;
- /** PXE base code protocol */
- EFI_PXE_BASE_CODE_PROTOCOL base;
- /** PXE base code mode */
- EFI_PXE_BASE_CODE_MODE mode;
-
- /** TCP/IP network-layer protocol */
- struct tcpip_net_protocol *tcpip;
- /** Network-layer protocol */
- struct net_protocol *net;
-
- /** Data transfer buffer */
- struct xfer_buffer buf;
-
- /** (M)TFTP download interface */
- struct interface tftp;
- /** Block size (for TFTP) */
- size_t blksize;
- /** Overall return status */
- int rc;
-
- /** UDP interface */
- struct interface udp;
- /** List of received UDP packets */
- struct list_head queue;
- /** UDP interface closer process */
- struct process process;
- };
-
- /**
- * Free PXE base code
- *
- * @v refcnt Reference count
- */
- static void efi_pxe_free ( struct refcnt *refcnt ) {
- struct efi_pxe *pxe = container_of ( refcnt, struct efi_pxe, refcnt );
-
- netdev_put ( pxe->netdev );
- free ( pxe );
- }
-
- /** List of PXE base codes */
- static LIST_HEAD ( efi_pxes );
-
- /**
- * Locate PXE base code
- *
- * @v handle EFI handle
- * @ret pxe PXE base code, or NULL
- */
- static struct efi_pxe * efi_pxe_find ( EFI_HANDLE handle ) {
- struct efi_pxe *pxe;
-
- /* Locate base code */
- list_for_each_entry ( pxe, &efi_pxes, list ) {
- if ( pxe->handle == handle )
- return pxe;
- }
-
- return NULL;
- }
-
- /******************************************************************************
- *
- * IP addresses
- *
- ******************************************************************************
- */
-
- /**
- * An EFI socket address
- *
- */
- struct sockaddr_efi {
- /** Socket address family (part of struct @c sockaddr) */
- sa_family_t se_family;
- /** Flags (part of struct @c sockaddr_tcpip) */
- uint16_t se_flags;
- /** TCP/IP port (part of struct @c sockaddr_tcpip) */
- uint16_t se_port;
- /** Scope ID (part of struct @c sockaddr_tcpip)
- *
- * For link-local or multicast addresses, this is the network
- * device index.
- */
- uint16_t se_scope_id;
- /** IP address */
- EFI_IP_ADDRESS se_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 ) /* se_family */ +
- sizeof ( uint16_t ) /* se_flags */ +
- sizeof ( uint16_t ) /* se_port */ +
- sizeof ( uint16_t ) /* se_scope_id */ +
- sizeof ( EFI_IP_ADDRESS ) /* se_addr */ ) ];
- } __attribute__ (( packed, may_alias ));
-
- /**
- * Populate socket address from EFI IP address
- *
- * @v pxe PXE base code
- * @v ip EFI IP address
- * @v sa Socket address to fill in
- */
- static void efi_pxe_ip_sockaddr ( struct efi_pxe *pxe, EFI_IP_ADDRESS *ip,
- struct sockaddr *sa ) {
- union {
- struct sockaddr sa;
- struct sockaddr_efi se;
- } *sockaddr = container_of ( sa, typeof ( *sockaddr ), sa );
-
- /* Initialise socket address */
- memset ( sockaddr, 0, sizeof ( *sockaddr ) );
- sockaddr->sa.sa_family = pxe->tcpip->sa_family;
- memcpy ( &sockaddr->se.se_addr, ip, pxe->net->net_addr_len );
- sockaddr->se.se_scope_id = pxe->netdev->index;
- }
-
- /**
- * Transcribe EFI IP address (for debugging)
- *
- * @v pxe PXE base code
- * @v ip EFI IP address
- * @ret text Transcribed IP address
- */
- static const char * efi_pxe_ip_ntoa ( struct efi_pxe *pxe,
- EFI_IP_ADDRESS *ip ) {
-
- return pxe->net->ntoa ( ip );
- }
-
- /**
- * Populate local IP address
- *
- * @v pxe PXE base code
- * @ret rc Return status code
- */
- static int efi_pxe_ip ( struct efi_pxe *pxe ) {
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
- struct in_addr address;
- struct in_addr netmask;
-
- /* It's unclear which of the potentially many IPv6 addresses
- * is supposed to be used.
- */
- if ( mode->UsingIpv6 )
- return -ENOTSUP;
-
- /* Fetch IP address and subnet mask */
- fetch_ipv4_setting ( netdev_settings ( pxe->netdev ), &ip_setting,
- &address );
- fetch_ipv4_setting ( netdev_settings ( pxe->netdev ), &netmask_setting,
- &netmask );
-
- /* Populate IP address and subnet mask */
- memset ( &mode->StationIp, 0, sizeof ( mode->StationIp ) );
- memcpy ( &mode->StationIp, &address, sizeof ( address ) );
- memset ( &mode->SubnetMask, 0, sizeof ( mode->SubnetMask ) );
- memcpy ( &mode->SubnetMask, &netmask, sizeof ( netmask ) );
-
- return 0;
- }
-
- /**
- * Check if IP address matches filter
- *
- * @v pxe PXE base code
- * @v ip EFI IP address
- * @ret is_match IP address matches filter
- */
- static int efi_pxe_ip_filter ( struct efi_pxe *pxe, EFI_IP_ADDRESS *ip ) {
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
- EFI_PXE_BASE_CODE_IP_FILTER *filter = &mode->IpFilter;
- uint8_t filters = filter->Filters;
- union {
- EFI_IP_ADDRESS ip;
- struct in_addr in;
- struct in6_addr in6;
- } *u = container_of ( ip, typeof ( *u ), ip );
- size_t addr_len = pxe->net->net_addr_len;
- unsigned int i;
-
- /* Match everything, if applicable */
- if ( filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS )
- return 1;
-
- /* Match all multicasts, if applicable */
- if ( filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST ) {
- if ( mode->UsingIpv6 ) {
- if ( IN6_IS_ADDR_MULTICAST ( &u->in6 ) )
- return 1;
- } else {
- if ( IN_IS_MULTICAST ( u->in.s_addr ) )
- return 1;
- }
- }
-
- /* Match IPv4 broadcasts, if applicable */
- if ( filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST ) {
- if ( ( ! mode->UsingIpv6 ) &&
- ( u->in.s_addr == INADDR_BROADCAST ) )
- return 1;
- }
-
- /* Match station address, if applicable */
- if ( filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP ) {
- if ( memcmp ( ip, &mode->StationIp, addr_len ) == 0 )
- return 1;
- }
-
- /* Match explicit addresses, if applicable */
- for ( i = 0 ; i < filter->IpCnt ; i++ ) {
- if ( memcmp ( ip, &filter->IpList[i], addr_len ) == 0 )
- return 1;
- }
-
- return 0;
- }
-
- /******************************************************************************
- *
- * Data transfer buffer
- *
- ******************************************************************************
- */
-
- /**
- * Reallocate PXE data transfer buffer
- *
- * @v xferbuf Data transfer buffer
- * @v len New length (or zero to free buffer)
- * @ret rc Return status code
- */
- static int efi_pxe_buf_realloc ( struct xfer_buffer *xferbuf __unused,
- size_t len __unused ) {
-
- /* Can never reallocate: return EFI_BUFFER_TOO_SMALL */
- return -ERANGE;
- }
-
- /**
- * Write data to PXE data transfer buffer
- *
- * @v xferbuf Data transfer buffer
- * @v offset Starting offset
- * @v data Data to copy
- * @v len Length of data
- */
- static void efi_pxe_buf_write ( struct xfer_buffer *xferbuf, size_t offset,
- const void *data, size_t len ) {
-
- /* Copy data to buffer */
- memcpy ( ( xferbuf->data + offset ), data, len );
- }
-
- /** PXE data transfer buffer operations */
- static struct xfer_buffer_operations efi_pxe_buf_operations = {
- .realloc = efi_pxe_buf_realloc,
- .write = efi_pxe_buf_write,
- };
-
- /******************************************************************************
- *
- * (M)TFTP download interface
- *
- ******************************************************************************
- */
-
- /**
- * Close PXE (M)TFTP download interface
- *
- * @v pxe PXE base code
- * @v rc Reason for close
- */
- static void efi_pxe_tftp_close ( struct efi_pxe *pxe, int rc ) {
-
- /* Restart interface */
- intf_restart ( &pxe->tftp, rc );
-
- /* Record overall status */
- pxe->rc = rc;
- }
-
- /**
- * Check PXE (M)TFTP download flow control window
- *
- * @v pxe PXE base code
- * @ret len Length of window
- */
- static size_t efi_pxe_tftp_window ( struct efi_pxe *pxe ) {
-
- /* Return requested blocksize */
- return pxe->blksize;
- }
-
- /**
- * Receive new PXE (M)TFTP download data
- *
- * @v pxe PXE base code
- * @v iobuf I/O buffer
- * @v meta Transfer metadata
- * @ret rc Return status code
- */
- static int efi_pxe_tftp_deliver ( struct efi_pxe *pxe,
- struct io_buffer *iobuf,
- struct xfer_metadata *meta ) {
- int rc;
-
- /* Deliver to data transfer buffer */
- if ( ( rc = xferbuf_deliver ( &pxe->buf, iob_disown ( iobuf ),
- meta ) ) != 0 )
- goto err_deliver;
-
- return 0;
-
- err_deliver:
- efi_pxe_tftp_close ( pxe, rc );
- return rc;
- }
-
- /** PXE file data transfer interface operations */
- static struct interface_operation efi_pxe_tftp_operations[] = {
- INTF_OP ( xfer_deliver, struct efi_pxe *, efi_pxe_tftp_deliver ),
- INTF_OP ( xfer_window, struct efi_pxe *, efi_pxe_tftp_window ),
- INTF_OP ( intf_close, struct efi_pxe *, efi_pxe_tftp_close ),
- };
-
- /** PXE file data transfer interface descriptor */
- static struct interface_descriptor efi_pxe_tftp_desc =
- INTF_DESC ( struct efi_pxe, tftp, efi_pxe_tftp_operations );
-
- /**
- * Open (M)TFTP download interface
- *
- * @v pxe PXE base code
- * @v ip EFI IP address
- * @v filename Filename
- * @ret rc Return status code
- */
- static int efi_pxe_tftp_open ( struct efi_pxe *pxe, EFI_IP_ADDRESS *ip,
- const char *filename ) {
- struct sockaddr server;
- struct uri *uri;
- int rc;
-
- /* Parse server address and filename */
- efi_pxe_ip_sockaddr ( pxe, ip, &server );
- uri = pxe_uri ( &server, filename );
- if ( ! uri ) {
- DBGC ( pxe, "PXE %s could not parse %s:%s\n", pxe->name,
- efi_pxe_ip_ntoa ( pxe, ip ), filename );
- rc = -ENOTSUP;
- goto err_parse;
- }
-
- /* Open URI */
- if ( ( rc = xfer_open_uri ( &pxe->tftp, uri ) ) != 0 ) {
- DBGC ( pxe, "PXE %s could not open: %s\n",
- pxe->name, strerror ( rc ) );
- goto err_open;
- }
-
- err_open:
- uri_put ( uri );
- err_parse:
- return rc;
- }
-
- /******************************************************************************
- *
- * UDP interface
- *
- ******************************************************************************
- */
-
- /** EFI UDP pseudo-header */
- struct efi_pxe_udp_pseudo_header {
- /** Network-layer protocol */
- struct net_protocol *net;
- /** Destination port */
- uint16_t dest_port;
- /** Source port */
- uint16_t src_port;
- } __attribute__ (( packed ));
-
- /**
- * Close UDP interface
- *
- * @v pxe PXE base code
- * @v rc Reason for close
- */
- static void efi_pxe_udp_close ( struct efi_pxe *pxe, int rc ) {
- struct io_buffer *iobuf;
- struct io_buffer *tmp;
-
- /* Release our claim on SNP devices, if applicable */
- if ( process_running ( &pxe->process ) )
- efi_snp_release();
-
- /* Stop process */
- process_del ( &pxe->process );
-
- /* Restart UDP interface */
- intf_restart ( &pxe->udp, rc );
-
- /* Flush any received UDP packets */
- list_for_each_entry_safe ( iobuf, tmp, &pxe->queue, list ) {
- list_del ( &iobuf->list );
- free_iob ( iobuf );
- }
- }
-
- /**
- * Receive UDP packet
- *
- * @v pxe PXE base code
- * @v iobuf I/O buffer
- * @v meta Data transfer metadata
- * @ret rc Return status code
- */
- static int efi_pxe_udp_deliver ( struct efi_pxe *pxe, struct io_buffer *iobuf,
- struct xfer_metadata *meta ) {
- struct sockaddr_efi *se_src;
- struct sockaddr_efi *se_dest;
- struct tcpip_net_protocol *tcpip;
- struct net_protocol *net;
- struct efi_pxe_udp_pseudo_header *pshdr;
- size_t addr_len;
- size_t pshdr_len;
- int rc;
-
- /* Sanity checks */
- assert ( meta != NULL );
- se_src = ( ( struct sockaddr_efi * ) meta->src );
- assert ( se_src != NULL );
- se_dest = ( ( struct sockaddr_efi * ) meta->dest );
- assert ( se_dest != NULL );
- assert ( se_src->se_family == se_dest->se_family );
-
- /* Determine protocol */
- tcpip = tcpip_net_protocol ( se_src->se_family );
- if ( ! tcpip ) {
- rc = -ENOTSUP;
- goto err_unsupported;
- }
- net = tcpip->net_protocol;
- addr_len = net->net_addr_len;
-
- /* Construct pseudo-header */
- pshdr_len = ( sizeof ( *pshdr ) + ( 2 * addr_len ) );
- if ( ( rc = iob_ensure_headroom ( iobuf, pshdr_len ) ) != 0 )
- goto err_headroom;
- memcpy ( iob_push ( iobuf, addr_len ), &se_src->se_addr, addr_len );
- memcpy ( iob_push ( iobuf, addr_len ), &se_dest->se_addr, addr_len );
- pshdr = iob_push ( iobuf, sizeof ( *pshdr ) );
- pshdr->net = net;
- pshdr->dest_port = ntohs ( se_dest->se_port );
- pshdr->src_port = ntohs ( se_src->se_port );
-
- /* Add to queue */
- list_add_tail ( &iobuf->list, &pxe->queue );
-
- return 0;
-
- err_unsupported:
- err_headroom:
- free_iob ( iobuf );
- return rc;
- }
-
- /** PXE UDP interface operations */
- static struct interface_operation efi_pxe_udp_operations[] = {
- INTF_OP ( xfer_deliver, struct efi_pxe *, efi_pxe_udp_deliver ),
- INTF_OP ( intf_close, struct efi_pxe *, efi_pxe_udp_close ),
- };
-
- /** PXE UDP interface descriptor */
- static struct interface_descriptor efi_pxe_udp_desc =
- INTF_DESC ( struct efi_pxe, udp, efi_pxe_udp_operations );
-
- /**
- * Open UDP interface
- *
- * @v pxe PXE base code
- * @ret rc Return status code
- */
- static int efi_pxe_udp_open ( struct efi_pxe *pxe ) {
- int rc;
-
- /* If interface is already open, then cancel the scheduled close */
- if ( process_running ( &pxe->process ) ) {
- process_del ( &pxe->process );
- return 0;
- }
-
- /* Open promiscuous UDP interface */
- if ( ( rc = udp_open_promisc ( &pxe->udp ) ) != 0 ) {
- DBGC ( pxe, "PXE %s could not open UDP connection: %s\n",
- pxe->name, strerror ( rc ) );
- return rc;
- }
-
- /* Claim network devices */
- efi_snp_claim();
-
- return 0;
- }
-
- /**
- * Schedule close of UDP interface
- *
- * @v pxe PXE base code
- */
- static void efi_pxe_udp_schedule_close ( struct efi_pxe *pxe ) {
-
- /* The EFI PXE base code protocol does not provide any
- * explicit UDP open/close methods. To avoid the overhead of
- * reopening a socket for each read/write operation, we start
- * a process which will close the socket immediately if the
- * next call into iPXE is anything other than a UDP
- * read/write.
- */
- process_add ( &pxe->process );
- }
-
- /**
- * Scheduled close of UDP interface
- *
- * @v pxe PXE base code
- */
- static void efi_pxe_udp_scheduled_close ( struct efi_pxe *pxe ) {
-
- /* Close UDP interface */
- efi_pxe_udp_close ( pxe, 0 );
- }
-
- /** UDP close process descriptor */
- static struct process_descriptor efi_pxe_process_desc =
- PROC_DESC_ONCE ( struct efi_pxe, process, efi_pxe_udp_scheduled_close );
-
- /******************************************************************************
- *
- * Fake DHCP packets
- *
- ******************************************************************************
- */
-
- /**
- * Name fake DHCP packet
- *
- * @v pxe PXE base code
- * @v packet Packet
- * @ret name Name of packet
- */
- static const char * efi_pxe_fake_name ( struct efi_pxe *pxe,
- EFI_PXE_BASE_CODE_PACKET *packet ) {
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
-
- if ( packet == &mode->DhcpDiscover ) {
- return "DhcpDiscover";
- } else if ( packet == &mode->DhcpAck ) {
- return "DhcpAck";
- } else if ( packet == &mode->ProxyOffer ) {
- return "ProxyOffer";
- } else if ( packet == &mode->PxeDiscover ) {
- return "PxeDiscover";
- } else if ( packet == &mode->PxeReply ) {
- return "PxeReply";
- } else if ( packet == &mode->PxeBisReply ) {
- return "PxeBisReply";
- } else {
- return "<UNKNOWN>";
- }
- }
-
- /**
- * Construct fake DHCP packet and flag
- *
- * @v pxe PXE base code
- * @v fake Fake packet constructor
- * @v packet Packet to fill in
- * @ret exists Packet existence flag
- */
- static BOOLEAN efi_pxe_fake ( struct efi_pxe *pxe,
- int ( * fake ) ( struct net_device *netdev,
- void *data, size_t len ),
- EFI_PXE_BASE_CODE_PACKET *packet ) {
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
- struct dhcp_packet dhcppkt;
- struct dhcphdr *dhcphdr;
- unsigned int len;
- int rc;
-
- /* The fake packet constructors do not support IPv6 */
- if ( mode->UsingIpv6 )
- return FALSE;
-
- /* Attempt to construct packet */
- if ( ( rc = fake ( pxe->netdev, packet, sizeof ( *packet ) ) != 0 ) ) {
- DBGC ( pxe, "PXE %s could not fake %s: %s\n", pxe->name,
- efi_pxe_fake_name ( pxe, packet ), strerror ( rc ) );
- return FALSE;
- }
-
- /* The WDS bootstrap wdsmgfw.efi has a buggy DHCPv4 packet
- * parser which does not correctly handle DHCP padding bytes.
- * Specifically, if a padding byte (i.e. a zero) is
- * encountered, the parse will first increment the pointer by
- * one to skip over the padding byte but will then drop into
- * the code path for handling normal options, which increments
- * the pointer by two to skip over the (already-skipped) type
- * field and the (non-existent) length field.
- *
- * The upshot of this bug in WDS is that the parser will fail
- * with an error 0xc0000023 if the number of spare bytes after
- * the end of the options is not an exact multiple of three.
- *
- * Work around this buggy parser by adding an explicit
- * DHCP_END tag.
- */
- dhcphdr = container_of ( &packet->Dhcpv4.BootpOpcode,
- struct dhcphdr, op );
- dhcppkt_init ( &dhcppkt, dhcphdr, sizeof ( *packet ) );
- len = dhcppkt_len ( &dhcppkt );
- if ( len < sizeof ( *packet ) )
- packet->Raw[len] = DHCP_END;
-
- return TRUE;
- }
-
- /**
- * Construct fake DHCP packets
- *
- * @v pxe PXE base code
- */
- static void efi_pxe_fake_all ( struct efi_pxe *pxe ) {
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
-
- /* Construct fake packets */
- mode->DhcpDiscoverValid =
- efi_pxe_fake ( pxe, create_fakedhcpdiscover,
- &mode->DhcpDiscover );
- mode->DhcpAckReceived =
- efi_pxe_fake ( pxe, create_fakedhcpack,
- &mode->DhcpAck );
- mode->PxeReplyReceived =
- efi_pxe_fake ( pxe, create_fakepxebsack,
- &mode->PxeReply );
- }
-
- /******************************************************************************
- *
- * Base code protocol
- *
- ******************************************************************************
- */
-
- /**
- * Start PXE base code
- *
- * @v base PXE base code protocol
- * @v use_ipv6 Use IPv6
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI efi_pxe_start ( EFI_PXE_BASE_CODE_PROTOCOL *base,
- BOOLEAN use_ipv6 ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
- struct tcpip_net_protocol *ipv6 = tcpip_net_protocol ( AF_INET6 );
- sa_family_t family = ( use_ipv6 ? AF_INET6 : AF_INET );
- int rc;
-
- DBGC ( pxe, "PXE %s START %s\n", pxe->name, ( ipv6 ? "IPv6" : "IPv4" ));
-
- /* Initialise mode structure */
- memset ( mode, 0, sizeof ( *mode ) );
- mode->AutoArp = TRUE;
- mode->TTL = DEFAULT_TTL;
- mode->ToS = DEFAULT_ToS;
- mode->IpFilter.Filters =
- ( EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP |
- EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST |
- EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS |
- EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST );
-
- /* Check for IPv4/IPv6 support */
- mode->Ipv6Supported = ( ipv6 != NULL );
- mode->Ipv6Available = ( ipv6 != NULL );
- pxe->tcpip = tcpip_net_protocol ( family );
- if ( ! pxe->tcpip ) {
- DBGC ( pxe, "PXE %s has no support for %s\n",
- pxe->name, socket_family_name ( family ) );
- return EFI_UNSUPPORTED;
- }
- pxe->net = pxe->tcpip->net_protocol;
- mode->UsingIpv6 = use_ipv6;
-
- /* Populate station IP address */
- if ( ( rc = efi_pxe_ip ( pxe ) ) != 0 )
- return rc;
-
- /* Construct fake DHCP packets */
- efi_pxe_fake_all ( pxe );
-
- /* Record that base code is started */
- mode->Started = TRUE;
- DBGC ( pxe, "PXE %s using %s\n",
- pxe->name, pxe->net->ntoa ( &mode->StationIp ) );
-
- return 0;
- }
-
- /**
- * Stop PXE base code
- *
- * @v base PXE base code protocol
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI efi_pxe_stop ( EFI_PXE_BASE_CODE_PROTOCOL *base ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
-
- DBGC ( pxe, "PXE %s STOP\n", pxe->name );
-
- /* Record that base code is stopped */
- mode->Started = FALSE;
-
- /* Close TFTP */
- efi_pxe_tftp_close ( pxe, 0 );
-
- /* Close UDP */
- efi_pxe_udp_close ( pxe, 0 );
-
- return 0;
- }
-
- /**
- * Perform DHCP
- *
- * @v base PXE base code protocol
- * @v sort Offers should be sorted
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI efi_pxe_dhcp ( EFI_PXE_BASE_CODE_PROTOCOL *base,
- BOOLEAN sort ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- struct net_device *netdev = pxe->netdev;
- int rc;
-
- DBGC ( pxe, "PXE %s DHCP %s\n",
- pxe->name, ( sort ? "sorted" : "unsorted" ) );
-
- /* Claim network devices */
- efi_snp_claim();
-
- /* Initiate configuration */
- if ( ( rc = netdev_configure_all ( netdev ) ) != 0 ) {
- DBGC ( pxe, "PXE %s could not initiate configuration: %s\n",
- pxe->name, strerror ( rc ) );
- goto err_configure;
- }
-
- /* Wait for configuration to complete (or time out) */
- while ( netdev_configuration_in_progress ( netdev ) )
- step();
-
- /* Report timeout if configuration failed */
- if ( ! netdev_configuration_ok ( netdev ) ) {
- rc = -ETIMEDOUT;
- goto err_timeout;
- }
-
- /* Update station IP address */
- if ( ( rc = efi_pxe_ip ( pxe ) ) != 0 )
- goto err_ip;
-
- /* Update faked DHCP packets */
- efi_pxe_fake_all ( pxe );
-
- err_ip:
- err_timeout:
- err_configure:
- efi_snp_release();
- return EFIRC ( rc );
- }
-
- /**
- * Perform boot server discovery
- *
- * @v base PXE base code protocol
- * @v type Boot server type
- * @v layer Boot server layer
- * @v bis Use boot integrity services
- * @v info Additional information
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_pxe_discover ( EFI_PXE_BASE_CODE_PROTOCOL *base, UINT16 type, UINT16 *layer,
- BOOLEAN bis, EFI_PXE_BASE_CODE_DISCOVER_INFO *info ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- EFI_IP_ADDRESS *ip;
- unsigned int i;
-
- DBGC ( pxe, "PXE %s DISCOVER type %d layer %d%s\n",
- pxe->name, type, *layer, ( bis ? " bis" : "" ) );
- if ( info ) {
- DBGC ( pxe, "%s%s%s%s %s",
- ( info->UseMCast ? " mcast" : "" ),
- ( info->UseBCast ? " bcast" : "" ),
- ( info->UseUCast ? " ucast" : "" ),
- ( info->MustUseList ? " list" : "" ),
- efi_pxe_ip_ntoa ( pxe, &info->ServerMCastIp ) );
- for ( i = 0 ; i < info->IpCnt ; i++ ) {
- ip = &info->SrvList[i].IpAddr;
- DBGC ( pxe, " %d%s:%s", info->SrvList[i].Type,
- ( info->SrvList[i].AcceptAnyResponse ?
- ":any" : "" ), efi_pxe_ip_ntoa ( pxe, ip ) );
- }
- }
- DBGC ( pxe, "\n" );
-
- /* Not used by any bootstrap I can find to test with */
- return EFI_UNSUPPORTED;
- }
-
- /**
- * Perform (M)TFTP
- *
- * @v base PXE base code protocol
- * @v opcode TFTP opcode
- * @v data Data buffer
- * @v overwrite Overwrite file
- * @v len Length of data buffer
- * @v blksize Block size
- * @v ip Server address
- * @v filename Filename
- * @v info Additional information
- * @v callback Pass packets to callback instead of data buffer
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_pxe_mtftp ( EFI_PXE_BASE_CODE_PROTOCOL *base,
- EFI_PXE_BASE_CODE_TFTP_OPCODE opcode, VOID *data,
- BOOLEAN overwrite, UINT64 *len, UINTN *blksize,
- EFI_IP_ADDRESS *ip, UINT8 *filename,
- EFI_PXE_BASE_CODE_MTFTP_INFO *info, BOOLEAN callback ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- int rc;
-
- DBGC ( pxe, "PXE %s MTFTP %d%s %p+%llx", pxe->name, opcode,
- ( overwrite ? " overwrite" : "" ), data, *len );
- if ( blksize )
- DBGC ( pxe, " blksize %zd", ( ( size_t ) *blksize ) );
- DBGC ( pxe, " %s:%s", efi_pxe_ip_ntoa ( pxe, ip ), filename );
- if ( info ) {
- DBGC ( pxe, " %s:%d:%d:%d:%d",
- efi_pxe_ip_ntoa ( pxe, &info->MCastIp ),
- info->CPort, info->SPort, info->ListenTimeout,
- info->TransmitTimeout );
- }
- DBGC ( pxe, "%s\n", ( callback ? " callback" : "" ) );
-
- /* Fail unless operation is supported */
- if ( ! ( ( opcode == EFI_PXE_BASE_CODE_TFTP_READ_FILE ) ||
- ( opcode == EFI_PXE_BASE_CODE_MTFTP_READ_FILE ) ) ) {
- DBGC ( pxe, "PXE %s unsupported MTFTP opcode %d\n",
- pxe->name, opcode );
- rc = -ENOTSUP;
- goto err_opcode;
- }
-
- /* Claim network devices */
- efi_snp_claim();
-
- /* Determine block size. Ignore the requested block size
- * unless we are using callbacks, since limiting HTTP to a
- * 512-byte TCP window is not sensible.
- */
- pxe->blksize = ( ( callback && blksize ) ? *blksize : -1UL );
-
- /* Initialise data transfer buffer */
- pxe->buf.data = data;
- pxe->buf.len = *len;
-
- /* Open download */
- if ( ( rc = efi_pxe_tftp_open ( pxe, ip,
- ( ( const char * ) filename ) ) ) != 0 )
- goto err_open;
-
- /* Wait for download to complete */
- pxe->rc = -EINPROGRESS;
- while ( pxe->rc == -EINPROGRESS )
- step();
- if ( ( rc = pxe->rc ) != 0 ) {
- DBGC ( pxe, "PXE %s download failed: %s\n",
- pxe->name, strerror ( rc ) );
- goto err_download;
- }
-
- err_download:
- efi_pxe_tftp_close ( pxe, rc );
- err_open:
- efi_snp_release();
- err_opcode:
- return EFIRC ( rc );
- }
-
- /**
- * Transmit UDP packet
- *
- * @v base PXE base code protocol
- * @v flags Operation flags
- * @v dest_ip Destination address
- * @v dest_port Destination port
- * @v gateway Gateway address
- * @v src_ip Source address
- * @v src_port Source port
- * @v hdr_len Header length
- * @v hdr Header data
- * @v len Length
- * @v data Data
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_pxe_udp_write ( EFI_PXE_BASE_CODE_PROTOCOL *base, UINT16 flags,
- EFI_IP_ADDRESS *dest_ip,
- EFI_PXE_BASE_CODE_UDP_PORT *dest_port,
- EFI_IP_ADDRESS *gateway, EFI_IP_ADDRESS *src_ip,
- EFI_PXE_BASE_CODE_UDP_PORT *src_port,
- UINTN *hdr_len, VOID *hdr, UINTN *len, VOID *data ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
- struct io_buffer *iobuf;
- struct xfer_metadata meta;
- union {
- struct sockaddr_tcpip st;
- struct sockaddr sa;
- } dest;
- union {
- struct sockaddr_tcpip st;
- struct sockaddr sa;
- } src;
- int rc;
-
- DBGC2 ( pxe, "PXE %s UDP WRITE ", pxe->name );
- if ( src_ip )
- DBGC2 ( pxe, "%s", efi_pxe_ip_ntoa ( pxe, src_ip ) );
- DBGC2 ( pxe, ":" );
- if ( src_port &&
- ( ! ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT ) ) ) {
- DBGC2 ( pxe, "%d", *src_port );
- } else {
- DBGC2 ( pxe, "*" );
- }
- DBGC2 ( pxe, "->%s:%d", efi_pxe_ip_ntoa ( pxe, dest_ip ), *dest_port );
- if ( gateway )
- DBGC2 ( pxe, " via %s", efi_pxe_ip_ntoa ( pxe, gateway ) );
- if ( hdr_len )
- DBGC2 ( pxe, " %p+%zx", hdr, ( ( size_t ) *hdr_len ) );
- DBGC2 ( pxe, " %p+%zx", data, ( ( size_t ) *len ) );
- if ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT )
- DBGC2 ( pxe, " frag" );
- DBGC2 ( pxe, "\n" );
-
- /* Open UDP connection (if applicable) */
- if ( ( rc = efi_pxe_udp_open ( pxe ) ) != 0 )
- goto err_open;
-
- /* Construct destination address */
- efi_pxe_ip_sockaddr ( pxe, dest_ip, &dest.sa );
- dest.st.st_port = htons ( *dest_port );
-
- /* Construct source address */
- efi_pxe_ip_sockaddr ( pxe, ( src_ip ? src_ip : &mode->StationIp ),
- &src.sa );
- if ( src_port &&
- ( ! ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT ) ) ) {
- src.st.st_port = htons ( *src_port );
- } else {
- /* The API does not allow for a sensible concept of
- * binding to a local port, so just use a random value.
- */
- src.st.st_port = ( random() | htons ( 1024 ) );
- if ( src_port )
- *src_port = ntohs ( src.st.st_port );
- }
-
- /* Allocate I/O buffer */
- iobuf = xfer_alloc_iob ( &pxe->udp,
- ( *len + ( hdr_len ? *hdr_len : 0 ) ) );
- if ( ! iobuf ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
-
- /* Populate I/O buffer */
- if ( hdr_len )
- memcpy ( iob_put ( iobuf, *hdr_len ), hdr, *hdr_len );
- memcpy ( iob_put ( iobuf, *len ), data, *len );
-
- /* Construct metadata */
- memset ( &meta, 0, sizeof ( meta ) );
- meta.src = &src.sa;
- meta.dest = &dest.sa;
- meta.netdev = pxe->netdev;
-
- /* Deliver I/O buffer */
- if ( ( rc = xfer_deliver ( &pxe->udp, iob_disown ( iobuf ),
- &meta ) ) != 0 ) {
- DBGC ( pxe, "PXE %s could not transmit: %s\n",
- pxe->name, strerror ( rc ) );
- goto err_deliver;
- }
-
- err_deliver:
- free_iob ( iobuf );
- err_alloc:
- efi_pxe_udp_schedule_close ( pxe );
- err_open:
- return EFIRC ( rc );
- }
-
- /**
- * Receive UDP packet
- *
- * @v base PXE base code protocol
- * @v flags Operation flags
- * @v dest_ip Destination address
- * @v dest_port Destination port
- * @v src_ip Source address
- * @v src_port Source port
- * @v hdr_len Header length
- * @v hdr Header data
- * @v len Length
- * @v data Data
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_pxe_udp_read ( EFI_PXE_BASE_CODE_PROTOCOL *base, UINT16 flags,
- EFI_IP_ADDRESS *dest_ip,
- EFI_PXE_BASE_CODE_UDP_PORT *dest_port,
- EFI_IP_ADDRESS *src_ip,
- EFI_PXE_BASE_CODE_UDP_PORT *src_port,
- UINTN *hdr_len, VOID *hdr, UINTN *len, VOID *data ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- struct io_buffer *iobuf;
- struct efi_pxe_udp_pseudo_header *pshdr;
- EFI_IP_ADDRESS *actual_dest_ip;
- EFI_IP_ADDRESS *actual_src_ip;
- size_t addr_len;
- size_t frag_len;
- int rc;
-
- DBGC2 ( pxe, "PXE %s UDP READ ", pxe->name );
- if ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER ) {
- DBGC2 ( pxe, "(filter)" );
- } else if ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP ) {
- DBGC2 ( pxe, "*" );
- } else if ( dest_ip ) {
- DBGC2 ( pxe, "%s", efi_pxe_ip_ntoa ( pxe, dest_ip ) );
- }
- DBGC2 ( pxe, ":" );
- if ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT ) {
- DBGC2 ( pxe, "*" );
- } else if ( dest_port ) {
- DBGC2 ( pxe, "%d", *dest_port );
- } else {
- DBGC2 ( pxe, "<NULL>" );
- }
- DBGC2 ( pxe, "<-" );
- if ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP ) {
- DBGC2 ( pxe, "*" );
- } else if ( src_ip ) {
- DBGC2 ( pxe, "%s", efi_pxe_ip_ntoa ( pxe, src_ip ) );
- } else {
- DBGC2 ( pxe, "<NULL>" );
- }
- DBGC2 ( pxe, ":" );
- if ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT ) {
- DBGC2 ( pxe, "*" );
- } else if ( src_port ) {
- DBGC2 ( pxe, "%d", *src_port );
- } else {
- DBGC2 ( pxe, "<NULL>" );
- }
- if ( hdr_len )
- DBGC2 ( pxe, " %p+%zx", hdr, ( ( size_t ) *hdr_len ) );
- DBGC2 ( pxe, " %p+%zx\n", data, ( ( size_t ) *len ) );
-
- /* Open UDP connection (if applicable) */
- if ( ( rc = efi_pxe_udp_open ( pxe ) ) != 0 )
- goto err_open;
-
- /* Try receiving a packet, if the queue is empty */
- if ( list_empty ( &pxe->queue ) )
- step();
-
- /* Remove first packet from the queue */
- iobuf = list_first_entry ( &pxe->queue, struct io_buffer, list );
- if ( ! iobuf ) {
- rc = -ETIMEDOUT; /* "no packet" */
- goto err_empty;
- }
- list_del ( &iobuf->list );
-
- /* Strip pseudo-header */
- pshdr = iobuf->data;
- addr_len = ( pshdr->net->net_addr_len );
- iob_pull ( iobuf, sizeof ( *pshdr ) );
- actual_dest_ip = iobuf->data;
- iob_pull ( iobuf, addr_len );
- actual_src_ip = iobuf->data;
- iob_pull ( iobuf, addr_len );
- DBGC2 ( pxe, "PXE %s UDP RX %s:%d", pxe->name,
- pshdr->net->ntoa ( actual_dest_ip ), pshdr->dest_port );
- DBGC2 ( pxe, "<-%s:%d len %#zx\n", pshdr->net->ntoa ( actual_src_ip ),
- pshdr->src_port, iob_len ( iobuf ) );
-
- /* Filter based on network-layer protocol */
- if ( pshdr->net != pxe->net ) {
- DBGC2 ( pxe, "PXE %s filtered out %s packet\n",
- pxe->name, pshdr->net->name );
- rc = -ETIMEDOUT; /* "no packet" */
- goto err_filter;
- }
-
- /* Filter based on port numbers */
- if ( ! ( ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT ) ||
- ( dest_port && ( *dest_port == pshdr->dest_port ) ) ) ) {
- DBGC2 ( pxe, "PXE %s filtered out destination port %d\n",
- pxe->name, pshdr->dest_port );
- rc = -ETIMEDOUT; /* "no packet" */
- goto err_filter;
- }
- if ( ! ( ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT ) ||
- ( src_port && ( *src_port == pshdr->src_port ) ) ) ) {
- DBGC2 ( pxe, "PXE %s filtered out source port %d\n",
- pxe->name, pshdr->src_port );
- rc = -ETIMEDOUT; /* "no packet" */
- goto err_filter;
- }
-
- /* Filter based on source IP address */
- if ( ! ( ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP ) ||
- ( src_ip &&
- ( memcmp ( src_ip, actual_src_ip, addr_len ) == 0 ) ) ) ) {
- DBGC2 ( pxe, "PXE %s filtered out source IP %s\n",
- pxe->name, pshdr->net->ntoa ( actual_src_ip ) );
- rc = -ETIMEDOUT; /* "no packet" */
- goto err_filter;
- }
-
- /* Filter based on destination IP address */
- if ( ! ( ( ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER ) &&
- efi_pxe_ip_filter ( pxe, actual_dest_ip ) ) ||
- ( ( ! ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER ) ) &&
- ( ( flags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP ) ||
- ( dest_ip && ( memcmp ( dest_ip, actual_dest_ip,
- addr_len ) == 0 ) ) ) ) ) ) {
- DBGC2 ( pxe, "PXE %s filtered out destination IP %s\n",
- pxe->name, pshdr->net->ntoa ( actual_dest_ip ) );
- rc = -ETIMEDOUT; /* "no packet" */
- goto err_filter;
- }
-
- /* Fill in addresses and port numbers */
- if ( dest_ip )
- memcpy ( dest_ip, actual_dest_ip, addr_len );
- if ( dest_port )
- *dest_port = pshdr->dest_port;
- if ( src_ip )
- memcpy ( src_ip, actual_src_ip, addr_len );
- if ( src_port )
- *src_port = pshdr->src_port;
-
- /* Fill in header, if applicable */
- if ( hdr_len ) {
- frag_len = iob_len ( iobuf );
- if ( frag_len > *hdr_len )
- frag_len = *hdr_len;
- memcpy ( hdr, iobuf->data, frag_len );
- iob_pull ( iobuf, frag_len );
- *hdr_len = frag_len;
- }
-
- /* Fill in data buffer */
- frag_len = iob_len ( iobuf );
- if ( frag_len > *len )
- frag_len = *len;
- memcpy ( data, iobuf->data, frag_len );
- iob_pull ( iobuf, frag_len );
- *len = frag_len;
-
- /* Check for overflow */
- if ( iob_len ( iobuf ) ) {
- rc = -ERANGE;
- goto err_too_short;
- }
-
- /* Success */
- rc = 0;
-
- err_too_short:
- err_filter:
- free_iob ( iobuf );
- err_empty:
- efi_pxe_udp_schedule_close ( pxe );
- err_open:
- return EFIRC ( rc );
- }
-
- /**
- * Set receive filter
- *
- * @v base PXE base code protocol
- * @v filter Receive filter
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_pxe_set_ip_filter ( EFI_PXE_BASE_CODE_PROTOCOL *base,
- EFI_PXE_BASE_CODE_IP_FILTER *filter ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
- unsigned int i;
-
- DBGC ( pxe, "PXE %s SET IP FILTER %02x",
- pxe->name, filter->Filters );
- for ( i = 0 ; i < filter->IpCnt ; i++ ) {
- DBGC ( pxe, " %s",
- efi_pxe_ip_ntoa ( pxe, &filter->IpList[i] ) );
- }
- DBGC ( pxe, "\n" );
-
- /* Update filter */
- memcpy ( &mode->IpFilter, filter, sizeof ( mode->IpFilter ) );
-
- return 0;
- }
-
- /**
- * Resolve MAC address
- *
- * @v base PXE base code protocol
- * @v ip IP address
- * @v mac MAC address to fill in
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI efi_pxe_arp ( EFI_PXE_BASE_CODE_PROTOCOL *base,
- EFI_IP_ADDRESS *ip,
- EFI_MAC_ADDRESS *mac ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
-
- DBGC ( pxe, "PXE %s ARP %s %p\n",
- pxe->name, efi_pxe_ip_ntoa ( pxe, ip ), mac );
-
- /* Not used by any bootstrap I can find to test with */
- return EFI_UNSUPPORTED;
- }
-
- /**
- * Set parameters
- *
- * @v base PXE base code protocol
- * @v autoarp Automatic ARP packet generation
- * @v sendguid Send GUID as client hardware address
- * @v ttl IP time to live
- * @v tos IP type of service
- * @v callback Make callbacks
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_pxe_set_parameters ( EFI_PXE_BASE_CODE_PROTOCOL *base,
- BOOLEAN *autoarp, BOOLEAN *sendguid, UINT8 *ttl,
- UINT8 *tos, BOOLEAN *callback ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
-
- DBGC ( pxe, "PXE %s SET PARAMETERS", pxe->name );
- if ( autoarp )
- DBGC ( pxe, " %s", ( *autoarp ? "autoarp" : "noautoarp" ) );
- if ( sendguid )
- DBGC ( pxe, " %s", ( *sendguid ? "sendguid" : "sendmac" ) );
- if ( ttl )
- DBGC ( pxe, " ttl %d", *ttl );
- if ( tos )
- DBGC ( pxe, " tos %d", *tos );
- if ( callback ) {
- DBGC ( pxe, " %s",
- ( *callback ? "callback" : "nocallback" ) );
- }
- DBGC ( pxe, "\n" );
-
- /* Update parameters */
- if ( autoarp )
- mode->AutoArp = *autoarp;
- if ( sendguid )
- mode->SendGUID = *sendguid;
- if ( ttl )
- mode->TTL = *ttl;
- if ( tos )
- mode->ToS = *tos;
- if ( callback )
- mode->MakeCallbacks = *callback;
-
- return 0;
- }
-
- /**
- * Set IP address
- *
- * @v base PXE base code protocol
- * @v ip IP address
- * @v netmask Subnet mask
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_pxe_set_station_ip ( EFI_PXE_BASE_CODE_PROTOCOL *base,
- EFI_IP_ADDRESS *ip, EFI_IP_ADDRESS *netmask ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
-
- DBGC ( pxe, "PXE %s SET STATION IP ", pxe->name );
- if ( ip )
- DBGC ( pxe, "%s", efi_pxe_ip_ntoa ( pxe, ip ) );
- if ( netmask )
- DBGC ( pxe, "/%s", efi_pxe_ip_ntoa ( pxe, netmask ) );
- DBGC ( pxe, "\n" );
-
- /* Update IP address and netmask */
- if ( ip )
- memcpy ( &mode->StationIp, ip, sizeof ( mode->StationIp ) );
- if ( netmask )
- memcpy ( &mode->SubnetMask, netmask, sizeof (mode->SubnetMask));
-
- return 0;
- }
-
- /**
- * Update cached DHCP packets
- *
- * @v base PXE base code protocol
- * @v dhcpdisc_ok DHCPDISCOVER is valid
- * @v dhcpack_ok DHCPACK received
- * @v proxyoffer_ok ProxyDHCPOFFER received
- * @v pxebsdisc_ok PxeBsDISCOVER valid
- * @v pxebsack_ok PxeBsACK received
- * @v pxebsbis_ok PxeBsBIS received
- * @v dhcpdisc DHCPDISCOVER packet
- * @v dhcpack DHCPACK packet
- * @v proxyoffer ProxyDHCPOFFER packet
- * @v pxebsdisc PxeBsDISCOVER packet
- * @v pxebsack PxeBsACK packet
- * @v pxebsbis PxeBsBIS packet
- * @ret efirc EFI status code
- */
- static EFI_STATUS EFIAPI
- efi_pxe_set_packets ( EFI_PXE_BASE_CODE_PROTOCOL *base, BOOLEAN *dhcpdisc_ok,
- BOOLEAN *dhcpack_ok, BOOLEAN *proxyoffer_ok,
- BOOLEAN *pxebsdisc_ok, BOOLEAN *pxebsack_ok,
- BOOLEAN *pxebsbis_ok, EFI_PXE_BASE_CODE_PACKET *dhcpdisc,
- EFI_PXE_BASE_CODE_PACKET *dhcpack,
- EFI_PXE_BASE_CODE_PACKET *proxyoffer,
- EFI_PXE_BASE_CODE_PACKET *pxebsdisc,
- EFI_PXE_BASE_CODE_PACKET *pxebsack,
- EFI_PXE_BASE_CODE_PACKET *pxebsbis ) {
- struct efi_pxe *pxe = container_of ( base, struct efi_pxe, base );
- EFI_PXE_BASE_CODE_MODE *mode = &pxe->mode;
-
- DBGC ( pxe, "PXE %s SET PACKETS\n", pxe->name );
-
- /* Update fake packet flags */
- if ( dhcpdisc_ok )
- mode->DhcpDiscoverValid = *dhcpdisc_ok;
- if ( dhcpack_ok )
- mode->DhcpAckReceived = *dhcpack_ok;
- if ( proxyoffer_ok )
- mode->ProxyOfferReceived = *proxyoffer_ok;
- if ( pxebsdisc_ok )
- mode->PxeDiscoverValid = *pxebsdisc_ok;
- if ( pxebsack_ok )
- mode->PxeReplyReceived = *pxebsack_ok;
- if ( pxebsbis_ok )
- mode->PxeBisReplyReceived = *pxebsbis_ok;
-
- /* Update fake packet contents */
- if ( dhcpdisc )
- memcpy ( &mode->DhcpDiscover, dhcpdisc, sizeof ( *dhcpdisc ) );
- if ( dhcpack )
- memcpy ( &mode->DhcpAck, dhcpack, sizeof ( *dhcpack ) );
- if ( proxyoffer )
- memcpy ( &mode->ProxyOffer, proxyoffer, sizeof ( *proxyoffer ));
- if ( pxebsdisc )
- memcpy ( &mode->PxeDiscover, pxebsdisc, sizeof ( *pxebsdisc ) );
- if ( pxebsack )
- memcpy ( &mode->PxeReply, pxebsack, sizeof ( *pxebsack ) );
- if ( pxebsbis )
- memcpy ( &mode->PxeBisReply, pxebsbis, sizeof ( *pxebsbis ) );
-
- return 0;
- }
-
- /** PXE base code protocol */
- static EFI_PXE_BASE_CODE_PROTOCOL efi_pxe_base_code_protocol = {
- .Revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION,
- .Start = efi_pxe_start,
- .Stop = efi_pxe_stop,
- .Dhcp = efi_pxe_dhcp,
- .Discover = efi_pxe_discover,
- .Mtftp = efi_pxe_mtftp,
- .UdpWrite = efi_pxe_udp_write,
- .UdpRead = efi_pxe_udp_read,
- .SetIpFilter = efi_pxe_set_ip_filter,
- .Arp = efi_pxe_arp,
- .SetParameters = efi_pxe_set_parameters,
- .SetStationIp = efi_pxe_set_station_ip,
- .SetPackets = efi_pxe_set_packets,
- };
-
- /**
- * Install PXE base code protocol
- *
- * @v handle EFI handle
- * @v netdev Underlying network device
- * @ret rc Return status code
- */
- int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct tcpip_net_protocol *ipv6 = tcpip_net_protocol ( AF_INET6 );
- struct efi_pxe *pxe;
- struct in_addr ip;
- BOOLEAN use_ipv6;
- EFI_STATUS efirc;
- int rc;
-
- /* Allocate and initialise structure */
- pxe = zalloc ( sizeof ( *pxe ) );
- if ( ! pxe ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- ref_init ( &pxe->refcnt, efi_pxe_free );
- pxe->netdev = netdev_get ( netdev );
- pxe->name = netdev->name;
- pxe->handle = handle;
- memcpy ( &pxe->base, &efi_pxe_base_code_protocol, sizeof ( pxe->base ));
- pxe->base.Mode = &pxe->mode;
- pxe->buf.op = &efi_pxe_buf_operations;
- intf_init ( &pxe->tftp, &efi_pxe_tftp_desc, &pxe->refcnt );
- intf_init ( &pxe->udp, &efi_pxe_udp_desc, &pxe->refcnt );
- INIT_LIST_HEAD ( &pxe->queue );
- process_init_stopped ( &pxe->process, &efi_pxe_process_desc,
- &pxe->refcnt );
-
- /* Crude heuristic: assume that we prefer to use IPv4 if we
- * have an IPv4 address for the network device, otherwise
- * prefer IPv6 (if available).
- */
- fetch_ipv4_setting ( netdev_settings ( netdev ), &ip_setting, &ip );
- use_ipv6 = ( ip.s_addr ? FALSE : ( ipv6 != NULL ) );
-
- /* Start base code */
- efi_pxe_start ( &pxe->base, use_ipv6 );
-
- /* Install PXE base code protocol */
- if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
- &handle, &efi_pxe_base_code_protocol_guid, &pxe->base,
- NULL ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( pxe, "PXE %s could not install base code protocol: %s\n",
- pxe->name, strerror ( rc ) );
- goto err_install_protocol;
- }
-
- /* Transfer reference to list and return */
- list_add_tail ( &pxe->list, &efi_pxes );
- DBGC ( pxe, "PXE %s installed for %s\n",
- pxe->name, efi_handle_name ( handle ) );
- return 0;
-
- bs->UninstallMultipleProtocolInterfaces (
- handle, &efi_pxe_base_code_protocol_guid, &pxe->base,
- NULL );
- err_install_protocol:
- ref_put ( &pxe->refcnt );
- err_alloc:
- return rc;
- }
-
- /**
- * Uninstall PXE base code protocol
- *
- * @v handle EFI handle
- */
- void efi_pxe_uninstall ( EFI_HANDLE handle ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- struct efi_pxe *pxe;
-
- /* Locate PXE base code */
- pxe = efi_pxe_find ( handle );
- if ( ! handle ) {
- DBG ( "PXE could not find base code for %s\n",
- efi_handle_name ( handle ) );
- return;
- }
-
- /* Stop base code */
- efi_pxe_stop ( &pxe->base );
-
- /* Uninstall PXE base code protocol */
- bs->UninstallMultipleProtocolInterfaces (
- handle, &efi_pxe_base_code_protocol_guid, &pxe->base,
- NULL );
-
- /* Remove from list and drop list's reference */
- list_del ( &pxe->list );
- ref_put ( &pxe->refcnt );
- }
|