iPXE
Data Structures | Functions | Variables
pxe_udp.c File Reference

PXE UDP API. More...

#include <string.h>
#include <byteswap.h>
#include <ipxe/iobuf.h>
#include <ipxe/xfer.h>
#include <ipxe/udp.h>
#include <ipxe/uaccess.h>
#include <ipxe/process.h>
#include <ipxe/netdevice.h>
#include <realmode.h>
#include <pxe.h>

Go to the source code of this file.

Data Structures

struct  pxe_udp_pseudo_header
 A PXE UDP pseudo-header. More...
struct  pxe_udp_connection
 A PXE UDP connection. More...

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static int pxe_udp_deliver (struct pxe_udp_connection *pxe_udp, struct io_buffer *iobuf, struct xfer_metadata *meta)
 Receive PXE UDP data.
static PXENV_EXIT_t pxenv_udp_open (struct s_PXENV_UDP_OPEN *pxenv_udp_open)
 UDP OPEN.
static PXENV_EXIT_t pxenv_udp_close (struct s_PXENV_UDP_CLOSE *pxenv_udp_close)
 UDP CLOSE.
static PXENV_EXIT_t pxenv_udp_write (struct s_PXENV_UDP_WRITE *pxenv_udp_write)
 UDP WRITE.
static PXENV_EXIT_t pxenv_udp_read (struct s_PXENV_UDP_READ *pxenv_udp_read)
 UDP READ.

Variables

static struct interface_operation pxe_udp_xfer_operations []
 PXE UDP data transfer interface operations.
static struct interface_descriptor pxe_udp_xfer_desc
 PXE UDP data transfer interface descriptor.
static struct pxe_udp_connection pxe_udp
 The PXE UDP connection.
struct pxe_api_call pxe_udp_api[] __pxe_api_call
 PXE UDP API.

Detailed Description

PXE UDP API.

Definition in file pxe_udp.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static int pxe_udp_deliver ( struct pxe_udp_connection pxe_udp,
struct io_buffer iobuf,
struct xfer_metadata meta 
) [static]

Receive PXE UDP data.

Parameters:
pxe_udpPXE UDP connection
iobufI/O buffer
metaData transfer metadata
Return values:
rcReturn status code

Receives a packet as part of the current pxenv_udp_read() operation.

Definition at line 76 of file pxe_udp.c.

References AF_INET, assert, pxe_udp_pseudo_header::d_port, DBG, xfer_metadata::dest, pxe_udp_pseudo_header::dest_ip, ENOMEM, free_iob(), iob_ensure_headroom(), iob_push, io_buffer::list, pxe_udp_connection::list, list_add_tail, rc, in_addr::s_addr, pxe_udp_pseudo_header::s_port, sockaddr_in::sin_addr, sockaddr_in::sin_family, sockaddr_in::sin_port, xfer_metadata::src, and pxe_udp_pseudo_header::src_ip.

                                                          {
        struct pxe_udp_pseudo_header *pshdr;
        struct sockaddr_in *sin_src;
        struct sockaddr_in *sin_dest;
        int rc;

        /* Extract metadata */
        assert ( meta );
        sin_src = ( struct sockaddr_in * ) meta->src;
        assert ( sin_src );
        assert ( sin_src->sin_family == AF_INET );
        sin_dest = ( struct sockaddr_in * ) meta->dest;
        assert ( sin_dest );
        assert ( sin_dest->sin_family == AF_INET );

        /* Construct pseudo-header */
        if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *pshdr ) ) ) != 0 ) {
                DBG ( "PXE could not prepend pseudo-header\n" );
                rc = -ENOMEM;
                goto drop;
        }
        pshdr = iob_push ( iobuf, sizeof ( *pshdr ) );
        pshdr->src_ip = sin_src->sin_addr.s_addr;
        pshdr->s_port = sin_src->sin_port;
        pshdr->dest_ip = sin_dest->sin_addr.s_addr;
        pshdr->d_port = sin_dest->sin_port;

        /* Add to queue */
        list_add_tail ( &iobuf->list, &pxe_udp->list );

        return 0;

 drop:
        free_iob ( iobuf );
        return rc;
}
static PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN pxenv_udp_open) [static]

UDP OPEN.

Parameters:
pxenv_udp_openPointer to a struct s_PXENV_UDP_OPEN
s_PXENV_UDP_OPEN::src_ipIP address of this station, or 0.0.0.0
Return values:
PXENV_EXIT_SUCCESSAlways
s_PXENV_UDP_OPEN::StatusPXE status code
Exceptions:
PXENV_STATUS_UDP_OPENUDP connection already open
PXENV_STATUS_OUT_OF_RESOURCESCould not open connection

Prepares the PXE stack for communication using pxenv_udp_write() and pxenv_udp_read().

The IP address supplied in s_PXENV_UDP_OPEN::src_ip will be recorded and used as the local station's IP address for all further communication, including communication by means other than pxenv_udp_write() and pxenv_udp_read(). (If s_PXENV_UDP_OPEN::src_ip is 0.0.0.0, the local station's IP address will remain unchanged.)

You can only have one open UDP connection at a time. This is not a meaningful restriction, since pxenv_udp_write() and pxenv_udp_read() allow you to specify arbitrary local and remote ports and an arbitrary remote address for each packet. According to the PXE specifiation, you cannot have a UDP connection open at the same time as a TFTP connection; this restriction does not apply to Etherboot.

On x86, you must set the s_PXE::StatusCallout field to a nonzero value before calling this function in protected mode. You cannot call this function with a 32-bit stack segment. (See the relevant implementation note for more details.)

Note:
The PXE specification does not make it clear whether the IP address supplied in s_PXENV_UDP_OPEN::src_ip should be used only for this UDP connection, or retained for all future communication. The latter seems more consistent with typical PXE stack behaviour.
Etherboot currently ignores the s_PXENV_UDP_OPEN::src_ip parameter.

Definition at line 175 of file pxe_udp.c.

References DBG, inet_ntoa(), intf_restart(), pxe_udp_connection::local, net_device::name, netdev_is_open(), netdev_open(), pxe_netdev, PXENV_EXIT_FAILURE, PXENV_EXIT_SUCCESS, PXENV_STATUS, PXENV_STATUS_SUCCESS, rc, in_addr::s_addr, sockaddr_in::sin_addr, s_PXENV_UDP_OPEN::src_ip, s_PXENV_UDP_OPEN::Status, strerror(), udp_open_promisc(), and pxe_udp_connection::xfer.

                                                                               {
        int rc;

        DBG ( "PXENV_UDP_OPEN" );

        /* Record source IP address */
        pxe_udp.local.sin_addr.s_addr = pxenv_udp_open->src_ip;
        DBG ( " %s\n", inet_ntoa ( pxe_udp.local.sin_addr ) );

        /* Open network device, if necessary */
        if ( pxe_netdev && ( ! netdev_is_open ( pxe_netdev ) ) &&
             ( ( rc = netdev_open ( pxe_netdev ) ) != 0 ) ) {
                DBG ( "PXENV_UDP_OPEN could not (implicitly) open %s: %s\n",
                      pxe_netdev->name, strerror ( rc ) );
                pxenv_udp_open->Status = PXENV_STATUS ( rc );
                return PXENV_EXIT_FAILURE;
        }

        /* Open promiscuous UDP connection */
        intf_restart ( &pxe_udp.xfer, 0 );
        if ( ( rc = udp_open_promisc ( &pxe_udp.xfer ) ) != 0 ) {
                DBG ( "PXENV_UDP_OPEN could not open promiscuous socket: %s\n",
                      strerror ( rc ) );
                pxenv_udp_open->Status = PXENV_STATUS ( rc );
                return PXENV_EXIT_FAILURE;
        }

        pxenv_udp_open->Status = PXENV_STATUS_SUCCESS;
        return PXENV_EXIT_SUCCESS;
}
static PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE pxenv_udp_close) [static]

UDP CLOSE.

Parameters:
pxenv_udp_closePointer to a struct s_PXENV_UDP_CLOSE
Return values:
PXENV_EXIT_SUCCESSAlways
s_PXENV_UDP_CLOSE::StatusPXE status code
Exceptions:
None-

Closes a UDP connection opened with pxenv_udp_open().

You can only have one open UDP connection at a time. You cannot have a UDP connection open at the same time as a TFTP connection. You cannot use pxenv_udp_close() to close a TFTP connection; use pxenv_tftp_close() instead.

On x86, you must set the s_PXE::StatusCallout field to a nonzero value before calling this function in protected mode. You cannot call this function with a 32-bit stack segment. (See the relevant implementation note for more details.)

Definition at line 228 of file pxe_udp.c.

References DBG, free_iob(), intf_restart(), io_buffer::list, pxe_udp_connection::list, list_del, list_for_each_entry_safe, PXENV_EXIT_SUCCESS, PXENV_STATUS_SUCCESS, s_PXENV_UDP_CLOSE::Status, and pxe_udp_connection::xfer.

                                                              {
        struct io_buffer *iobuf;
        struct io_buffer *tmp;

        DBG ( "PXENV_UDP_CLOSE\n" );

        /* Close UDP connection */
        intf_restart ( &pxe_udp.xfer, 0 );

        /* Discard any received packets */
        list_for_each_entry_safe ( iobuf, tmp, &pxe_udp.list, list ) {
                list_del ( &iobuf->list );
                free_iob ( iobuf );
        }

        pxenv_udp_close->Status = PXENV_STATUS_SUCCESS;
        return PXENV_EXIT_SUCCESS;
}
static PXENV_EXIT_t pxenv_udp_write ( struct s_PXENV_UDP_WRITE pxenv_udp_write) [static]

UDP WRITE.

Parameters:
pxenv_udp_writePointer to a struct s_PXENV_UDP_WRITE
s_PXENV_UDP_WRITE::ipDestination IP address
s_PXENV_UDP_WRITE::gwRelay agent IP address, or 0.0.0.0
s_PXENV_UDP_WRITE::src_portSource UDP port, or 0
s_PXENV_UDP_WRITE::dst_portDestination UDP port
s_PXENV_UDP_WRITE::buffer_sizeLength of the UDP payload
s_PXENV_UDP_WRITE::bufferAddress of the UDP payload
Return values:
PXENV_EXIT_SUCCESSPacket was transmitted successfully
PXENV_EXIT_FAILUREPacket could not be transmitted
s_PXENV_UDP_WRITE::StatusPXE status code
Exceptions:
PXENV_STATUS_UDP_CLOSEDUDP connection is not open
PXENV_STATUS_UNDI_TRANSMIT_ERRORCould not transmit packet

Transmits a single UDP packet. A valid IP and UDP header will be prepended to the payload in s_PXENV_UDP_WRITE::buffer; the buffer should not contain precomputed IP and UDP headers, nor should it contain space allocated for these headers. The first byte of the buffer will be transmitted as the first byte following the UDP header.

If s_PXENV_UDP_WRITE::gw is 0.0.0.0, normal IP routing will take place. See the relevant implementation note for more details.

If s_PXENV_UDP_WRITE::src_port is 0, port 2069 will be used.

You must have opened a UDP connection with pxenv_udp_open() before calling pxenv_udp_write().

On x86, you must set the s_PXE::StatusCallout field to a nonzero value before calling this function in protected mode. You cannot call this function with a 32-bit stack segment. (See the relevant implementation note for more details.)

Note:
Etherboot currently ignores the s_PXENV_UDP_WRITE::gw parameter.

Definition at line 289 of file pxe_udp.c.

References AF_INET, buffer, s_PXENV_UDP_WRITE::buffer, s_PXENV_UDP_WRITE::buffer_size, copy_from_user(), DBG, dest, s_PXENV_UDP_WRITE::dst_port, htons, inet_ntoa(), iob_put, s_PXENV_UDP_WRITE::ip, len, pxe_udp_connection::local, memset(), ntohs, pxe_netdev, PXENV_EXIT_FAILURE, PXENV_EXIT_SUCCESS, PXENV_STATUS, PXENV_STATUS_OUT_OF_RESOURCES, PXENV_STATUS_SUCCESS, rc, real_to_user(), in_addr::s_addr, sockaddr_in::sin_addr, sockaddr_in::sin_family, sockaddr_in::sin_port, xfer_metadata::src, s_PXENV_UDP_WRITE::src_port, s_PXENV_UDP_WRITE::Status, strerror(), pxe_udp_connection::xfer, xfer_alloc_iob(), and xfer_deliver().

                                                              {
        struct sockaddr_in dest;
        struct xfer_metadata meta = {
                .src = ( struct sockaddr * ) &pxe_udp.local,
                .dest = ( struct sockaddr * ) &dest,
                .netdev = pxe_netdev,
        };
        size_t len;
        struct io_buffer *iobuf;
        userptr_t buffer;
        int rc;

        DBG ( "PXENV_UDP_WRITE" );

        /* Construct destination socket address */
        memset ( &dest, 0, sizeof ( dest ) );
        dest.sin_family = AF_INET;
        dest.sin_addr.s_addr = pxenv_udp_write->ip;
        dest.sin_port = pxenv_udp_write->dst_port;

        /* Set local (source) port.  PXE spec says source port is 2069
         * if not specified.  Really, this ought to be set at UDP open
         * time but hey, we didn't design this API.
         */
        pxe_udp.local.sin_port = pxenv_udp_write->src_port;
        if ( ! pxe_udp.local.sin_port )
                pxe_udp.local.sin_port = htons ( 2069 );

        /* FIXME: we ignore the gateway specified, since we're
         * confident of being able to do our own routing.  We should
         * probably allow for multiple gateways.
         */

        /* Allocate and fill data buffer */
        len = pxenv_udp_write->buffer_size;
        iobuf = xfer_alloc_iob ( &pxe_udp.xfer, len );
        if ( ! iobuf ) {
                DBG ( " out of memory\n" );
                pxenv_udp_write->Status = PXENV_STATUS_OUT_OF_RESOURCES;
                return PXENV_EXIT_FAILURE;
        }
        buffer = real_to_user ( pxenv_udp_write->buffer.segment,
                                pxenv_udp_write->buffer.offset );
        copy_from_user ( iob_put ( iobuf, len ), buffer, 0, len );

        DBG ( " %04x:%04x+%x %d->%s:%d\n", pxenv_udp_write->buffer.segment,
              pxenv_udp_write->buffer.offset, pxenv_udp_write->buffer_size,
              ntohs ( pxenv_udp_write->src_port ),
              inet_ntoa ( dest.sin_addr ),
              ntohs ( pxenv_udp_write->dst_port ) );
        
        /* Transmit packet */
        if ( ( rc = xfer_deliver ( &pxe_udp.xfer, iobuf, &meta ) ) != 0 ) {
                DBG ( "PXENV_UDP_WRITE could not transmit: %s\n",
                      strerror ( rc ) );
                pxenv_udp_write->Status = PXENV_STATUS ( rc );
                return PXENV_EXIT_FAILURE;
        }

        pxenv_udp_write->Status = PXENV_STATUS_SUCCESS;
        return PXENV_EXIT_SUCCESS;
}
static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ pxenv_udp_read) [static]

UDP READ.

Parameters:
pxenv_udp_readPointer to a struct s_PXENV_UDP_READ
s_PXENV_UDP_READ::dest_ipDestination IP address, or 0.0.0.0
s_PXENV_UDP_READ::d_portDestination UDP port, or 0
s_PXENV_UDP_READ::buffer_sizeSize of the UDP payload buffer
s_PXENV_UDP_READ::bufferAddress of the UDP payload buffer
Return values:
PXENV_EXIT_SUCCESSA packet has been received
PXENV_EXIT_FAILURENo packet has been received
s_PXENV_UDP_READ::StatusPXE status code
s_PXENV_UDP_READ::src_ipSource IP address
s_PXENV_UDP_READ::dest_ipDestination IP address
s_PXENV_UDP_READ::s_portSource UDP port
s_PXENV_UDP_READ::d_portDestination UDP port
s_PXENV_UDP_READ::buffer_sizeLength of UDP payload
Exceptions:
PXENV_STATUS_UDP_CLOSEDUDP connection is not open
PXENV_STATUS_FAILURENo packet was ready to read

Receive a single UDP packet. This is a non-blocking call; if no packet is ready to read, the call will return instantly with s_PXENV_UDP_READ::Status==PXENV_STATUS_FAILURE.

If s_PXENV_UDP_READ::dest_ip is 0.0.0.0, UDP packets addressed to any IP address will be accepted and may be returned to the caller.

If s_PXENV_UDP_READ::d_port is 0, UDP packets addressed to any UDP port will be accepted and may be returned to the caller.

You must have opened a UDP connection with pxenv_udp_open() before calling pxenv_udp_read().

On x86, you must set the s_PXE::StatusCallout field to a nonzero value before calling this function in protected mode. You cannot call this function with a 32-bit stack segment. (See the relevant implementation note for more details.)

Note:
The PXE specification (version 2.1) does not state that we should fill in s_PXENV_UDP_READ::dest_ip and s_PXENV_UDP_READ::d_port, but Microsoft Windows' NTLDR program expects us to do so, and will fail if we don't.

Definition at line 395 of file pxe_udp.c.

References assert, buffer, s_PXENV_UDP_READ::buffer, s_PXENV_UDP_READ::buffer_size, copy_to_user(), pxe_udp_pseudo_header::d_port, s_PXENV_UDP_READ::d_port, d_port, io_buffer::data, DBG, DBG2, pxe_udp_pseudo_header::dest_ip, s_PXENV_UDP_READ::dest_ip, free_iob(), htons, inet_ntoa(), iob_len(), iob_pull, len, io_buffer::list, pxe_udp_connection::list, list_del, list_empty, list_first_entry, ntohs, PXENV_EXIT_FAILURE, PXENV_EXIT_SUCCESS, PXENV_STATUS_FAILURE, PXENV_STATUS_SUCCESS, real_to_user(), in_addr::s_addr, pxe_udp_pseudo_header::s_port, s_PXENV_UDP_READ::s_port, pxe_udp_pseudo_header::src_ip, s_PXENV_UDP_READ::src_ip, s_PXENV_UDP_READ::Status, and step().

                                                                               {
        struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip };
        struct in_addr dest_ip;
        struct io_buffer *iobuf;
        struct pxe_udp_pseudo_header *pshdr;
        uint16_t d_port_wanted = pxenv_udp_read->d_port;
        uint16_t d_port;
        userptr_t buffer;
        size_t len;

        /* Try receiving a packet, if the queue is empty */
        if ( list_empty ( &pxe_udp.list ) )
                step();

        /* Remove first packet from the queue */
        iobuf = list_first_entry ( &pxe_udp.list, struct io_buffer, list );
        if ( ! iobuf ) {
                /* No packet received */
                DBG2 ( "PXENV_UDP_READ\n" );
                goto no_packet;
        }
        list_del ( &iobuf->list );

        /* Strip pseudo-header */
        assert ( iob_len ( iobuf ) >= sizeof ( *pshdr ) );
        pshdr = iobuf->data;
        iob_pull ( iobuf, sizeof ( *pshdr ) );
        dest_ip.s_addr = pshdr->dest_ip;
        d_port = pshdr->d_port;
        DBG ( "PXENV_UDP_READ" );

        /* Filter on destination address and/or port */
        if ( dest_ip_wanted.s_addr &&
             ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) {
                DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) );
                DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) );
                goto drop;
        }
        if ( d_port_wanted && ( d_port_wanted != d_port ) ) {
                DBG ( " wrong port %d", htons ( d_port ) );
                DBG ( " (wanted %d)\n", htons ( d_port_wanted ) );
                goto drop;
        }

        /* Copy packet to buffer and record length */
        buffer = real_to_user ( pxenv_udp_read->buffer.segment,
                                pxenv_udp_read->buffer.offset );
        len = iob_len ( iobuf );
        if ( len > pxenv_udp_read->buffer_size )
                len = pxenv_udp_read->buffer_size;
        copy_to_user ( buffer, 0, iobuf->data, len );
        pxenv_udp_read->buffer_size = len;

        /* Fill in source/dest information */
        pxenv_udp_read->src_ip = pshdr->src_ip;
        pxenv_udp_read->s_port = pshdr->s_port;
        pxenv_udp_read->dest_ip = pshdr->dest_ip;
        pxenv_udp_read->d_port = pshdr->d_port;

        DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment,
              pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size,
              inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) ));
        DBG ( "%d<-%s:%d\n",  ntohs ( pxenv_udp_read->s_port ),
              inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ),
              ntohs ( pxenv_udp_read->d_port ) );

        /* Free I/O buffer */
        free_iob ( iobuf );

        pxenv_udp_read->Status = PXENV_STATUS_SUCCESS;
        return PXENV_EXIT_SUCCESS;

 drop:
        free_iob ( iobuf );
 no_packet:
        pxenv_udp_read->Status = PXENV_STATUS_FAILURE;
        return PXENV_EXIT_FAILURE;
}

Variable Documentation

Initial value:

PXE UDP data transfer interface operations.

Definition at line 116 of file pxe_udp.c.

Initial value:

PXE UDP data transfer interface descriptor.

Definition at line 121 of file pxe_udp.c.

struct pxe_udp_connection pxe_udp [static]
Initial value:
 {
        .xfer = INTF_INIT ( pxe_udp_xfer_desc ),
        .local = {
                .sin_family = AF_INET,
        },
        .list = LIST_HEAD_INIT ( pxe_udp.list ),
}

The PXE UDP connection.

Definition at line 125 of file pxe_udp.c.

struct pxe_api_call pxe_udp_api [] __pxe_api_call