iPXE
Defines | Functions | Variables
peerdisc.c File Reference

Peer Content Caching and Retrieval (PeerDist) protocol peer discovery. More...

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/xfer.h>
#include <ipxe/iobuf.h>
#include <ipxe/open.h>
#include <ipxe/tcpip.h>
#include <ipxe/uuid.h>
#include <ipxe/base16.h>
#include <ipxe/netdevice.h>
#include <ipxe/timer.h>
#include <ipxe/fault.h>
#include <ipxe/pccrd.h>
#include <ipxe/peerdisc.h>

Go to the source code of this file.

Defines

#define PEERDISC_REPEAT_COUNT   2
 Number of repeated discovery attempts.
#define PEERDISC_REPEAT_TIMEOUT   ( 1 * TICKS_PER_SEC )
 Time between repeated discovery attempts.
#define PEERDISC_DEFAULT_TIMEOUT_SECS   2
 Default discovery timeout (in seconds)

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static LIST_HEAD (peerdisc_segments)
 List of discovery segments.
static struct peerdisc_segmentpeerdisc_find (const char *id)
 Find PeerDist discovery segment.
static int peerdisc_discovered (struct peerdisc_segment *segment, const char *location)
 Add discovered PeerDist peer.
void peerdisc_stat (struct interface *intf, struct peerdisc_peer *peer, struct list_head *peers)
 Report peer discovery statistics.
static int peerdisc_socket_open (void)
 Open all PeerDist discovery sockets.
static void peerdisc_socket_tx (const char *uuid, const char *id)
 Attempt to transmit PeerDist discovery requests on all sockets.
static int peerdisc_socket_rx (struct peerdisc_socket *socket, struct io_buffer *iobuf, struct xfer_metadata *meta __unused)
 Handle received PeerDist discovery reply.
static void peerdisc_socket_close (int rc)
 Close all PeerDist discovery sockets.
static void peerdisc_free (struct refcnt *refcnt)
 Free PeerDist discovery segment.
static void peerdisc_expired (struct retry_timer *timer, int over __unused)
 Handle discovery timer expiry.
static struct peerdisc_segmentpeerdisc_create (const char *id)
 Create PeerDist discovery segment.
static void peerdisc_destroy (struct peerdisc_segment *segment)
 Destroy PeerDist discovery segment.
int peerdisc_open (struct peerdisc_client *peerdisc, const void *id, size_t len)
 Open PeerDist discovery client.
void peerdisc_close (struct peerdisc_client *peerdisc)
 Close PeerDist discovery client.

Variables

unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS
 Recommended discovery timeout (in seconds)
static struct interface_operation peerdisc_socket_operations []
 PeerDist discovery socket interface operations.
static struct interface_descriptor peerdisc_socket_desc
 PeerDist discovery socket interface descriptor.
struct peerdisc_socket
peerdisc_socket_ipv4 
__peerdisc_socket
 PeerDist discovery IPv4 socket.

Detailed Description

Peer Content Caching and Retrieval (PeerDist) protocol peer discovery.

Definition in file peerdisc.c.


Define Documentation

#define PEERDISC_REPEAT_COUNT   2

Number of repeated discovery attempts.

Definition at line 53 of file peerdisc.c.

Referenced by peerdisc_expired().

#define PEERDISC_REPEAT_TIMEOUT   ( 1 * TICKS_PER_SEC )

Time between repeated discovery attempts.

Definition at line 56 of file peerdisc.c.

Referenced by peerdisc_expired().

Default discovery timeout (in seconds)

Definition at line 59 of file peerdisc.c.

Referenced by peerdisc_socket_rx().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static LIST_HEAD ( peerdisc_segments  ) [static]

List of discovery segments.

static struct peerdisc_segment * peerdisc_find ( const char *  id) [static, read]

Find PeerDist discovery segment.

Parameters:
idSegment ID
Return values:
segmentPeerDist discovery segment, or NULL if not found

Definition at line 358 of file peerdisc.c.

References peerdisc_segment::id, peerdisc_segment::list, list_for_each_entry, NULL, segment, and strcmp().

Referenced by peerdisc_open(), and peerdisc_socket_rx().

                                                                  {
        struct peerdisc_segment *segment;

        /* Look for a matching segment */
        list_for_each_entry ( segment, &peerdisc_segments, list ) {
                if ( strcmp ( id, segment->id ) == 0 )
                        return segment;
        }

        return NULL;
}
static int peerdisc_discovered ( struct peerdisc_segment segment,
const char *  location 
) [static]

Add discovered PeerDist peer.

Parameters:
segmentPeerDist discovery segment
locationPeer location
Return values:
rcReturn status code

Definition at line 377 of file peerdisc.c.

References peerdisc_segment::clients, DBGC2, peerdisc_client_operations::discovered, ENOMEM, peerdisc_peer::list, peerdisc_client::list, list_add_tail, list_for_each_entry, list_for_each_entry_safe, peerdisc_peer::location, peerdisc_client::op, peerdisc_segment::peers, strcmp(), strcpy(), strlen(), and zalloc().

Referenced by peerdisc_socket_rx().

                                                        {
        struct peerdisc_peer *peer;
        struct peerdisc_client *peerdisc;
        struct peerdisc_client *tmp;

        /* Ignore duplicate peers */
        list_for_each_entry ( peer, &segment->peers, list ) {
                if ( strcmp ( peer->location, location ) == 0 ) {
                        DBGC2 ( segment, "PEERDISC %p duplicate %s\n",
                                segment, location );
                        return 0;
                }
        }
        DBGC2 ( segment, "PEERDISC %p discovered %s\n", segment, location );

        /* Allocate and initialise structure */
        peer = zalloc ( sizeof ( *peer ) + strlen ( location ) + 1 /* NUL */ );
        if ( ! peer )
                return -ENOMEM;
        strcpy ( peer->location, location );

        /* Add to end of list of peers */
        list_add_tail ( &peer->list, &segment->peers );

        /* Notify all clients */
        list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list )
                peerdisc->op->discovered ( peerdisc );

        return 0;
}
void peerdisc_stat ( struct interface intf,
struct peerdisc_peer peer,
struct list_head peers 
)

Report peer discovery statistics.

Parameters:
intfInterface
peerSelected peer (or NULL)
peersList of available peers

Definition at line 93 of file peerdisc.c.

References dest, intf_get_dest_op, intf_object(), intf_put(), op, peerdisc_stat(), and peerdisc_stat_TYPE.

Referenced by peerblk_done(), and peerdisc_stat().

                                               {
        struct interface *dest;
        peerdisc_stat_TYPE ( void * ) *op =
                intf_get_dest_op ( intf, peerdisc_stat, &dest );
        void *object = intf_object ( dest );

        if ( op ) {
                op ( object, peer, peers );
        } else {
                /* Default is to do nothing */
        }

        intf_put ( dest );
}
static int peerdisc_socket_open ( void  ) [static]

Open all PeerDist discovery sockets.

Return values:
rcReturn status code

Definition at line 121 of file peerdisc.c.

References peerdisc_socket::address, DBGC, for_each_table_entry, for_each_table_entry_continue_reverse, intf_restart(), peerdisc_socket::name, NULL, PEERDISC_SOCKETS, rc, peerdisc_socket::sa, SOCK_DGRAM, strerror(), peerdisc_socket::xfer, and xfer_open_socket().

Referenced by peerdisc_open().

                                         {
        struct peerdisc_socket *socket;
        int rc;

        /* Open each socket */
        for_each_table_entry ( socket, PEERDISC_SOCKETS ) {
                if ( ( rc = xfer_open_socket ( &socket->xfer, SOCK_DGRAM,
                                               &socket->address.sa,
                                               NULL ) ) != 0 ) {
                        DBGC ( socket, "PEERDISC %s could not open socket: "
                               "%s\n", socket->name, strerror ( rc ) );
                        goto err;
                }
        }

        return 0;

 err:
        for_each_table_entry_continue_reverse ( socket, PEERDISC_SOCKETS )
                intf_restart ( &socket->xfer, rc );
        return rc;
}
static void peerdisc_socket_tx ( const char *  uuid,
const char *  id 
) [static]

Attempt to transmit PeerDist discovery requests on all sockets.

Parameters:
uuidMessage UUID string
idSegment identifier string

Definition at line 150 of file peerdisc.c.

References peerdisc_socket::address, address, DBGC, xfer_metadata::dest, for_each_netdev, for_each_table_entry, free, net_device::index, len, memcpy(), memset(), peerdisc_socket::name, net_device::name, netdev, netdev_is_open(), PEERDISC_DISCARD_RATE, PEERDISC_SOCKETS, peerdist_discovery_request(), rc, request, peerdisc_socket::sa, strerror(), strlen(), peerdisc_socket::xfer, and xfer_deliver_raw_meta().

Referenced by peerdisc_expired().

                                                                    {
        struct peerdisc_socket *socket;
        struct net_device *netdev;
        struct xfer_metadata meta;
        union {
                struct sockaddr sa;
                struct sockaddr_tcpip st;
        } address;
        char *request;
        size_t len;
        int rc;

        /* Construct discovery request */
        request = peerdist_discovery_request ( uuid, id );
        if ( ! request )
                goto err_request;
        len = strlen ( request );

        /* Initialise data transfer metadata */
        memset ( &meta, 0, sizeof ( meta ) );
        meta.dest = &address.sa;

        /* Send message on each socket */
        for_each_table_entry ( socket, PEERDISC_SOCKETS ) {

                /* Initialise socket address */
                memcpy ( &address.sa, &socket->address.sa,
                         sizeof ( address.sa ) );

                /* Send message on each open network device */
                for_each_netdev ( netdev ) {

                        /* Skip unopened network devices */
                        if ( ! netdev_is_open ( netdev ) )
                                continue;
                        address.st.st_scope_id = netdev->index;

                        /* Discard request (for test purposes) if applicable */
                        if ( inject_fault ( PEERDISC_DISCARD_RATE ) )
                                continue;

                        /* Transmit request */
                        if ( ( rc = xfer_deliver_raw_meta ( &socket->xfer,
                                                            request, len,
                                                            &meta ) ) != 0 ) {
                                DBGC ( socket, "PEERDISC %s could not transmit "
                                       "via %s: %s\n", socket->name,
                                       netdev->name, strerror ( rc ) );
                                /* Contine to try other net devices/sockets */
                                continue;
                        }
                }
        }

        free ( request );
 err_request:
        return;
}
static int peerdisc_socket_rx ( struct peerdisc_socket socket,
struct io_buffer iobuf,
struct xfer_metadata *meta  __unused 
) [static]

Handle received PeerDist discovery reply.

Parameters:
socketPeerDist discovery socket
iobufI/O buffer
metaData transfer metadata
Return values:
rcReturn status code

Definition at line 217 of file peerdisc.c.

References io_buffer::data, DBGC, DBGC_HDA, free_iob(), id, peerdist_discovery_reply::ids, iob_len(), peerdist_discovery_reply::locations, peerdisc_socket::name, PEERDISC_DEFAULT_TIMEOUT_SECS, PEERDISC_DISCARD_RATE, peerdisc_discovered(), peerdisc_find(), peerdisc_timeout_secs, rc, segment, strerror(), and strlen().

                                                                      {
        struct peerdist_discovery_reply reply;
        struct peerdisc_segment *segment;
        char *id;
        char *location;
        int rc;

        /* Discard reply (for test purposes) if applicable */
        if ( ( rc = inject_fault ( PEERDISC_DISCARD_RATE ) ) != 0 )
                goto err;

        /* Parse reply */
        if ( ( rc = peerdist_discovery_reply ( iobuf->data, iob_len ( iobuf ),
                                               &reply ) ) != 0 ) {
                DBGC ( socket, "PEERDISC %s could not parse reply: %s\n",
                       socket->name, strerror ( rc ) );
                DBGC_HDA ( socket, 0, iobuf->data, iob_len ( iobuf ) );
                goto err;
        }

        /* Any kind of discovery reply indicates that there are active
         * peers on a local network, so restore the recommended
         * discovery timeout to its default value for future requests.
         */
        if ( peerdisc_timeout_secs != PEERDISC_DEFAULT_TIMEOUT_SECS ) {
                DBGC ( socket, "PEERDISC %s restoring timeout to %d seconds\n",
                       socket->name, PEERDISC_DEFAULT_TIMEOUT_SECS );
        }
        peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS;

        /* Iterate over segment IDs */
        for ( id = reply.ids ; *id ; id += ( strlen ( id ) + 1 /* NUL */ ) ) {

                /* Find corresponding segment */
                segment = peerdisc_find ( id );
                if ( ! segment ) {
                        DBGC ( socket, "PEERDISC %s ignoring reply for %s\n",
                               socket->name, id );
                        continue;
                }

                /* Report all discovered peer locations */
                for ( location = reply.locations ; *location ;
                      location += ( strlen ( location ) + 1 /* NUL */ ) ) {

                        /* Report discovered peer location */
                        if ( ( rc = peerdisc_discovered ( segment,
                                                          location ) ) != 0 )
                                goto err;
                }
        }

 err:
        free_iob ( iobuf );
        return rc;
}
static void peerdisc_socket_close ( int  rc) [static]

Close all PeerDist discovery sockets.

Parameters:
rcReason for close

Definition at line 281 of file peerdisc.c.

References for_each_table_entry, intf_restart(), PEERDISC_SOCKETS, and peerdisc_socket::xfer.

Referenced by peerdisc_close().

                                             {
        struct peerdisc_socket *socket;

        /* Close all sockets */
        for_each_table_entry ( socket, PEERDISC_SOCKETS )
                intf_restart ( &socket->xfer, rc );
}
static void peerdisc_free ( struct refcnt refcnt) [static]

Free PeerDist discovery segment.

Parameters:
refcntReference count

Definition at line 336 of file peerdisc.c.

References container_of, free, peerdisc_peer::list, list_del, list_for_each_entry_safe, peerdisc_segment::peers, and segment.

Referenced by peerdisc_create().

                                                    {
        struct peerdisc_segment *segment =
                container_of ( refcnt, struct peerdisc_segment, refcnt );
        struct peerdisc_peer *peer;
        struct peerdisc_peer *tmp;

        /* Free all discovered peers */
        list_for_each_entry_safe ( peer, tmp, &segment->peers, list ) {
                list_del ( &peer->list );
                free ( peer );
        }

        /* Free segment */
        free ( segment );
}
static void peerdisc_expired ( struct retry_timer timer,
int over  __unused 
) [static]

Handle discovery timer expiry.

Parameters:
timerDiscovery timer
overFailure indicator

Definition at line 415 of file peerdisc.c.

References container_of, retry_timer::count, peerdisc_segment::id, PEERDISC_REPEAT_COUNT, PEERDISC_REPEAT_TIMEOUT, peerdisc_socket_tx(), segment, start_timer_fixed(), peerdisc_segment::timer, and peerdisc_segment::uuid.

Referenced by peerdisc_create().

                                                                              {
        struct peerdisc_segment *segment =
                container_of ( timer, struct peerdisc_segment, timer );

        /* Attempt to transmit discovery requests */
        peerdisc_socket_tx ( segment->uuid, segment->id );

        /* Schedule next transmission, if applicable */
        if ( timer->count < PEERDISC_REPEAT_COUNT )
                start_timer_fixed ( &segment->timer, PEERDISC_REPEAT_TIMEOUT );
}
static struct peerdisc_segment* peerdisc_create ( const char *  id) [static, read]

Create PeerDist discovery segment.

Parameters:
idSegment ID
Return values:
segmentPeerDist discovery segment, or NULL on error

Definition at line 433 of file peerdisc.c.

References peerdisc_segment::clients, DBGC2, dword, peerdisc_segment::id, INIT_LIST_HEAD, peerdisc_segment::list, list_add_tail, memcpy(), NULL, peerdisc_expired(), peerdisc_free(), peerdisc_segment::peers, random(), ref_init, peerdisc_segment::refcnt, segment, start_timer_nodelay(), strlen(), peerdisc_segment::timer, peerdisc_segment::uuid, uuid, uuid_ntoa(), and zalloc().

Referenced by peerdisc_open().

                                                                    {
        struct peerdisc_segment *segment;
        union {
                union uuid uuid;
                uint32_t dword[ sizeof ( union uuid ) / sizeof ( uint32_t ) ];
        } random_uuid;
        size_t uuid_len;
        size_t id_len;
        const char *uuid;
        char *uuid_copy;
        char *id_copy;
        unsigned int i;

        /* Generate a random message UUID.  This does not require high
         * quality randomness.
         */
        for ( i = 0 ; i < ( sizeof ( random_uuid.dword ) /
                            sizeof ( random_uuid.dword[0] ) ) ; i++ )
                random_uuid.dword[i] = random();
        uuid = uuid_ntoa ( &random_uuid.uuid );

        /* Calculate string lengths */
        id_len = ( strlen ( id ) + 1 /* NUL */ );
        uuid_len = ( strlen ( uuid ) + 1 /* NUL */ );

        /* Allocate and initialise structure */
        segment = zalloc ( sizeof ( *segment ) + id_len + uuid_len );
        if ( ! segment )
                return NULL;
        id_copy = ( ( ( void * ) segment ) + sizeof ( *segment ) );
        memcpy ( id_copy, id, id_len );
        uuid_copy = ( ( ( void * ) id_copy ) + id_len );
        memcpy ( uuid_copy, uuid, uuid_len );
        ref_init ( &segment->refcnt, peerdisc_free );
        segment->id = id_copy;
        segment->uuid = uuid_copy;
        INIT_LIST_HEAD ( &segment->peers );
        INIT_LIST_HEAD ( &segment->clients );
        timer_init ( &segment->timer, peerdisc_expired, &segment->refcnt );
        DBGC2 ( segment, "PEERDISC %p discovering %s\n", segment, segment->id );

        /* Start discovery timer */
        start_timer_nodelay ( &segment->timer );

        /* Add to list of segments, transfer reference to list, and return */
        list_add_tail ( &segment->list, &peerdisc_segments );
        return segment;
}
static void peerdisc_destroy ( struct peerdisc_segment segment) [static]

Destroy PeerDist discovery segment.

Parameters:
segmentPeerDist discovery segment

Definition at line 487 of file peerdisc.c.

References assert, peerdisc_segment::clients, peerdisc_segment::list, list_del, list_empty, ref_put, peerdisc_segment::refcnt, stop_timer(), and peerdisc_segment::timer.

Referenced by peerdisc_close().

                                                                  {

        /* Sanity check */
        assert ( list_empty ( &segment->clients ) );

        /* Stop timer */
        stop_timer ( &segment->timer );

        /* Remove from list of segments and drop list's reference */
        list_del ( &segment->list );
        ref_put ( &segment->refcnt );
}
int peerdisc_open ( struct peerdisc_client peerdisc,
const void *  id,
size_t  len 
)

Open PeerDist discovery client.

Parameters:
peerdiscPeerDist discovery client
idSegment ID
lenLength of segment ID
Return values:
rcReturn status code

Definition at line 515 of file peerdisc.c.

References assert, base16_encoded_len(), peerdisc_segment::clients, ENOMEM, id_string, peerdisc_client::list, list_add_tail, list_empty, NULL, peerdisc_create(), peerdisc_find(), peerdisc_socket_open(), rc, ref_get, peerdisc_segment::refcnt, peerdisc_client::segment, segment, and toupper().

Referenced by peerblk_open().

                                 {
        struct peerdisc_segment *segment;
        char id_string[ base16_encoded_len ( len ) + 1 /* NUL */ ];
        char *id_chr;
        int rc;

        /* Construct ID string */
        base16_encode ( id, len, id_string, sizeof ( id_string ) );
        for ( id_chr = id_string ; *id_chr ; id_chr++ )
                *id_chr = toupper ( *id_chr );

        /* Sanity check */
        assert ( peerdisc->segment == NULL );

        /* Open socket if this is the first segment */
        if ( list_empty ( &peerdisc_segments ) &&
             ( ( rc = peerdisc_socket_open() ) != 0 ) )
                return rc;

        /* Find or create segment */
        if ( ! ( ( segment = peerdisc_find ( id_string ) ) ||
                 ( segment = peerdisc_create ( id_string ) ) ) )
                return -ENOMEM;

        /* Add to list of clients */
        ref_get ( &segment->refcnt );
        peerdisc->segment = segment;
        list_add_tail ( &peerdisc->list, &segment->clients );

        return 0;
}
void peerdisc_close ( struct peerdisc_client peerdisc)

Close PeerDist discovery client.

Parameters:
peerdiscPeerDist discovery client

Definition at line 553 of file peerdisc.c.

References peerdisc_segment::clients, DBGC, peerdisc_client::list, list_del, list_empty, NULL, peerdisc_destroy(), peerdisc_socket_close(), peerdisc_timeout_secs, peerdisc_segment::peers, ref_put, peerdisc_segment::refcnt, peerdisc_client::segment, and segment.

Referenced by peerblk_close().

                                                         {
        struct peerdisc_segment *segment = peerdisc->segment;

        /* Ignore if discovery is already closed */
        if ( ! segment )
                return;

        /* If no peers were discovered, reduce the recommended
         * discovery timeout to minimise delays on future requests.
         */
        if ( list_empty ( &segment->peers ) && peerdisc_timeout_secs ) {
                peerdisc_timeout_secs--;
                DBGC ( segment, "PEERDISC %p reducing timeout to %d "
                       "seconds\n", peerdisc, peerdisc_timeout_secs );
        }

        /* Remove from list of clients */
        peerdisc->segment = NULL;
        list_del ( &peerdisc->list );
        ref_put ( &segment->refcnt );

        /* If this was the last clients, destroy the segment */
        if ( list_empty ( &segment->clients ) )
                peerdisc_destroy ( segment );

        /* If there are no more segments, close the socket */
        if ( list_empty ( &peerdisc_segments ) )
                peerdisc_socket_close ( 0 );
}

Variable Documentation

Recommended discovery timeout (in seconds)

We reduce the recommended discovery timeout whenever a segment fails to discover any peers, and restore the default value whenever a valid discovery reply is received. We continue to send discovery requests even if the recommended timeout is reduced to zero.

This strategy is intended to minimise discovery delays when no peers are available on the network, while allowing downloads to quickly switch back to using PeerDist acceleration if new peers become available.

Definition at line 73 of file peerdisc.c.

Referenced by peerblk_open(), peerdisc_close(), and peerdisc_socket_rx().

Initial value:

PeerDist discovery socket interface operations.

Definition at line 290 of file peerdisc.c.

Initial value:

PeerDist discovery socket interface descriptor.

Definition at line 295 of file peerdisc.c.

struct peerdisc_socket peerdisc_socket_ipv6 __peerdisc_socket
Initial value:
 {
        .name = "IPv4",
        .address = {
                .sin = {
                        .sin_family = AF_INET,
                        .sin_port = htons ( PEERDIST_DISCOVERY_PORT ),
                        .sin_addr.s_addr = htonl ( PEERDIST_DISCOVERY_IPV4 ),
                },
        },
        .xfer = INTF_INIT ( peerdisc_socket_desc ),
}

PeerDist discovery IPv4 socket.

PeerDist discovery IPv6 socket.

Definition at line 299 of file peerdisc.c.