iPXE
Data Structures | Defines | Enumerations | Functions | Variables
dhcpv6.c File Reference

Dynamic Host Configuration Protocol for IPv6. More...

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <byteswap.h>
#include <ipxe/interface.h>
#include <ipxe/xfer.h>
#include <ipxe/iobuf.h>
#include <ipxe/open.h>
#include <ipxe/netdevice.h>
#include <ipxe/settings.h>
#include <ipxe/retry.h>
#include <ipxe/timer.h>
#include <ipxe/in.h>
#include <ipxe/crc32.h>
#include <ipxe/errortab.h>
#include <ipxe/ipv6.h>
#include <ipxe/dhcp_arch.h>
#include <ipxe/dhcpv6.h>

Go to the source code of this file.

Data Structures

struct  dhcpv6_option_list
 A DHCPv6 option list. More...
struct  dhcpv6_settings
 A DHCPv6 settings block. More...
struct  dhcpv6_session_state
 A DHCPv6 session state. More...
struct  dhcpv6_session
 A DHCPv6 session. More...

Defines

#define EPROTO_UNSPECFAIL   __einfo_error ( EINFO_EPROTO_UNSPECFAIL )
#define EINFO_EPROTO_UNSPECFAIL   __einfo_uniqify ( EINFO_EPROTO, 1, "Unspecified server failure" )
#define EPROTO_NOADDRSAVAIL   __einfo_error ( EINFO_EPROTO_NOADDRSAVAIL )
#define EINFO_EPROTO_NOADDRSAVAIL   __einfo_uniqify ( EINFO_EPROTO, 2, "No addresses available" )
#define EPROTO_NOBINDING   __einfo_error ( EINFO_EPROTO_NOBINDING )
#define EINFO_EPROTO_NOBINDING   __einfo_uniqify ( EINFO_EPROTO, 3, "Client record unavailable" )
#define EPROTO_NOTONLINK   __einfo_error ( EINFO_EPROTO_NOTONLINK )
#define EINFO_EPROTO_NOTONLINK   __einfo_uniqify ( EINFO_EPROTO, 4, "Prefix not on link" )
#define EPROTO_USEMULTICAST   __einfo_error ( EINFO_EPROTO_USEMULTICAST )
#define EINFO_EPROTO_USEMULTICAST   __einfo_uniqify ( EINFO_EPROTO, 5, "Use multicast address" )
#define EPROTO_STATUS(status)

Enumerations

enum  dhcpv6_session_state_flags { DHCPV6_TX_IA_NA = 0x01, DHCPV6_TX_IAADDR = 0x02, DHCPV6_RX_RECORD_SERVER_ID = 0x04, DHCPV6_RX_RECORD_IAADDR = 0x08 }
 DHCPv6 session state flags. More...

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static union dhcpv6_any_optiondhcpv6_option (struct dhcpv6_option_list *options, unsigned int code)
 Find DHCPv6 option.
static int dhcpv6_check_duid (struct dhcpv6_option_list *options, unsigned int code, const void *expected, size_t len)
 Check DHCPv6 client or server identifier.
static int dhcpv6_status_code (struct dhcpv6_option_list *options)
 Get DHCPv6 status code.
static int dhcpv6_iaaddr (struct dhcpv6_option_list *options, uint32_t iaid, struct in6_addr *address)
 Get DHCPv6 identity association address.
static int dhcpv6_applies (struct settings *settings __unused, const struct setting *setting)
 Check applicability of DHCPv6 setting.
static int dhcpv6_fetch_lease (struct dhcpv6_settings *dhcpv6set, void *data, size_t len)
 Fetch value of DHCPv6 leased address.
static int dhcpv6_fetch (struct settings *settings, struct setting *setting, void *data, size_t len)
 Fetch value of DHCPv6 setting.
static int dhcpv6_register (struct in6_addr *lease, struct dhcpv6_option_list *options, struct settings *parent)
 Register DHCPv6 options as network device settings.
static const char * dhcpv6_type_name (unsigned int type)
 Name a DHCPv6 packet type.
static void dhcpv6_free (struct refcnt *refcnt)
 Free DHCPv6 session.
static void dhcpv6_finished (struct dhcpv6_session *dhcpv6, int rc)
 Terminate DHCPv6 session.
static void dhcpv6_set_state (struct dhcpv6_session *dhcpv6, struct dhcpv6_session_state *state)
 Transition to new DHCPv6 session state.
static size_t dhcpv6_user_class (void *data, size_t len)
 Get DHCPv6 user class.
static int dhcpv6_tx (struct dhcpv6_session *dhcpv6)
 Transmit current request.
static void dhcpv6_timer_expired (struct retry_timer *timer, int fail)
 Handle timer expiry.
static int dhcpv6_rx (struct dhcpv6_session *dhcpv6, struct io_buffer *iobuf, struct xfer_metadata *meta)
 Receive new data.
int start_dhcpv6 (struct interface *job, struct net_device *netdev, int stateful)
 Start DHCPv6.
struct setting filename6_setting __setting (SETTING_BOOT, filename)
 Boot filename setting.
struct setting dnssl6_setting __setting (SETTING_IP_EXTRA, dnssl)
 DNS search list setting.

Variables

struct errortab dhcpv6_errors[] __errortab
 Human-readable error messages.
static struct settings_operations dhcpv6_settings_operations
 DHCPv6 settings operations.
static uint8_t dhcpv6_request_options_data []
 Raw option data for options common to all DHCPv6 requests.
static struct dhcpv6_session_state dhcpv6_request
 DHCPv6 request state.
static struct dhcpv6_session_state dhcpv6_solicit
 DHCPv6 solicitation state.
static struct dhcpv6_session_state dhcpv6_information_request
 DHCPv6 information request state.
static struct interface_operation dhcpv6_job_op []
 DHCPv6 job control interface operations.
static struct interface_descriptor dhcpv6_job_desc
 DHCPv6 job control interface descriptor.
static struct interface_operation dhcpv6_xfer_op []
 DHCPv6 data transfer interface operations.
static struct interface_descriptor dhcpv6_xfer_desc
 DHCPv6 data transfer interface descriptor.

Detailed Description

Dynamic Host Configuration Protocol for IPv6.

Definition in file dhcpv6.c.


Define Documentation

Definition at line 53 of file dhcpv6.c.

#define EINFO_EPROTO_UNSPECFAIL   __einfo_uniqify ( EINFO_EPROTO, 1, "Unspecified server failure" )

Definition at line 54 of file dhcpv6.c.

Definition at line 56 of file dhcpv6.c.

#define EINFO_EPROTO_NOADDRSAVAIL   __einfo_uniqify ( EINFO_EPROTO, 2, "No addresses available" )

Definition at line 57 of file dhcpv6.c.

Definition at line 59 of file dhcpv6.c.

#define EINFO_EPROTO_NOBINDING   __einfo_uniqify ( EINFO_EPROTO, 3, "Client record unavailable" )

Definition at line 60 of file dhcpv6.c.

Definition at line 62 of file dhcpv6.c.

#define EINFO_EPROTO_NOTONLINK   __einfo_uniqify ( EINFO_EPROTO, 4, "Prefix not on link" )

Definition at line 63 of file dhcpv6.c.

Definition at line 65 of file dhcpv6.c.

#define EINFO_EPROTO_USEMULTICAST   __einfo_uniqify ( EINFO_EPROTO, 5, "Use multicast address" )

Definition at line 66 of file dhcpv6.c.

#define EPROTO_STATUS (   status)

Enumeration Type Documentation

DHCPv6 session state flags.

Enumerator:
DHCPV6_TX_IA_NA 

Include identity association within request.

DHCPV6_TX_IAADDR 

Include leased IPv6 address within request.

DHCPV6_RX_RECORD_SERVER_ID 

Record received server ID.

DHCPV6_RX_RECORD_IAADDR 

Record received IPv6 address.

Definition at line 456 of file dhcpv6.c.

                                {
        /** Include identity association within request */
        DHCPV6_TX_IA_NA = 0x01,
        /** Include leased IPv6 address within request */
        DHCPV6_TX_IAADDR = 0x02,
        /** Record received server ID */
        DHCPV6_RX_RECORD_SERVER_ID = 0x04,
        /** Record received IPv6 address */
        DHCPV6_RX_RECORD_IAADDR = 0x08,
};

Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static union dhcpv6_any_option* dhcpv6_option ( struct dhcpv6_option_list options,
unsigned int  code 
) [static, write]

Find DHCPv6 option.

Parameters:
optionsDHCPv6 option list
codeOption code
Return values:
optionDHCPv6 option, or NULL if not found

Definition at line 100 of file dhcpv6.c.

References dhcpv6_option::code, dhcpv6_option::data, dhcpv6_option_list::data, dhcpv6_any_option::header, htons, dhcpv6_option::len, dhcpv6_option_list::len, ntohs, and NULL.

Referenced by dhcpv6_check_duid(), dhcpv6_fetch(), dhcpv6_iaaddr(), dhcpv6_rx(), and dhcpv6_status_code().

                                                                        {
        const union dhcpv6_any_option *option = options->data;
        size_t remaining = options->len;
        size_t data_len;

        /* Scan through list of options */
        while ( remaining >= sizeof ( option->header ) ) {

                /* Calculate and validate option length */
                remaining -= sizeof ( option->header );
                data_len = ntohs ( option->header.len );
                if ( data_len > remaining ) {
                        /* Malformed option list */
                        return NULL;
                }

                /* Return if we have found the specified option */
                if ( option->header.code == htons ( code ) )
                        return option;

                /* Otherwise, move to the next option */
                option = ( ( ( void * ) option->header.data ) + data_len );
                remaining -= data_len;
        }

        return NULL;
}
static int dhcpv6_check_duid ( struct dhcpv6_option_list options,
unsigned int  code,
const void *  expected,
size_t  len 
) [static]

Check DHCPv6 client or server identifier.

Parameters:
optionsDHCPv6 option list
codeOption code
expectedExpected value
lenLength of expected value
Return values:
rcReturn status code

Definition at line 137 of file dhcpv6.c.

References dhcpv6_option(), dhcpv6_duid_option::duid, dhcpv6_any_option::duid, EINVAL, ENOENT, dhcpv6_duid_option::header, dhcpv6_option::len, memcmp(), and ntohs.

Referenced by dhcpv6_rx().

                                            {
        const union dhcpv6_any_option *option;
        const struct dhcpv6_duid_option *duid;

        /* Find option */
        option = dhcpv6_option ( options, code );
        if ( ! option )
                return -ENOENT;
        duid = &option->duid;

        /* Check option length */
        if ( ntohs ( duid->header.len ) != len )
                return -EINVAL;

        /* Compare option value */
        if ( memcmp ( duid->duid, expected, len ) != 0 )
                return -EINVAL;

        return 0;
}
static int dhcpv6_status_code ( struct dhcpv6_option_list options) [static]

Get DHCPv6 status code.

Parameters:
optionsDHCPv6 option list
Return values:
rcReturn status code

Definition at line 166 of file dhcpv6.c.

References dhcpv6_option(), DHCPV6_STATUS_CODE, EINVAL, EPROTO_STATUS, dhcpv6_status_code_option::header, dhcpv6_option::len, ntohs, status, dhcpv6_status_code_option::status, and dhcpv6_any_option::status_code.

Referenced by dhcpv6_iaaddr(), and dhcpv6_rx().

                                                                     {
        const union dhcpv6_any_option *option;
        const struct dhcpv6_status_code_option *status_code;
        unsigned int status;

        /* Find status code option, if present */
        option = dhcpv6_option ( options, DHCPV6_STATUS_CODE );
        if ( ! option ) {
                /* Omitted status code should be treated as "success" */
                return 0;
        }
        status_code = &option->status_code;

        /* Sanity check */
        if ( ntohs ( status_code->header.len ) <
             ( sizeof ( *status_code ) - sizeof ( status_code->header ) ) ) {
                return -EINVAL;
        }

        /* Calculate iPXE error code from DHCPv6 status code */
        status = ntohs ( status_code->status );
        return ( status ? -EPROTO_STATUS ( status ) : 0 );
}
static int dhcpv6_iaaddr ( struct dhcpv6_option_list options,
uint32_t  iaid,
struct in6_addr address 
) [static]

Get DHCPv6 identity association address.

Parameters:
optionsDHCPv6 option list
iaidIdentity association ID
addressIPv6 address to fill in
Return values:
rcReturn status code

Definition at line 198 of file dhcpv6.c.

References dhcpv6_iaaddr_option::address, dhcpv6_option_list::data, DHCPV6_IA_NA, DHCPV6_IAADDR, dhcpv6_option(), dhcpv6_status_code(), EINVAL, ENOENT, dhcpv6_ia_na_option::header, dhcpv6_iaaddr_option::header, htonl, dhcpv6_any_option::ia_na, dhcpv6_any_option::iaaddr, dhcpv6_ia_na_option::iaid, dhcpv6_option::len, dhcpv6_option_list::len, len, memcpy(), ntohs, offsetof, dhcpv6_ia_na_option::options, dhcpv6_iaaddr_option::options, options, and rc.

Referenced by dhcpv6_rx().

                                                      {
        const union dhcpv6_any_option *option;
        const struct dhcpv6_ia_na_option *ia_na;
        const struct dhcpv6_iaaddr_option *iaaddr;
        struct dhcpv6_option_list suboptions;
        size_t len;
        int rc;

        /* Find identity association option, if present */
        option = dhcpv6_option ( options, DHCPV6_IA_NA );
        if ( ! option )
                return -ENOENT;
        ia_na = &option->ia_na;

        /* Sanity check */
        len = ntohs ( ia_na->header.len );
        if ( len < ( sizeof ( *ia_na ) - sizeof ( ia_na->header ) ) )
                return -EINVAL;

        /* Check identity association ID */
        if ( ia_na->iaid != htonl ( iaid ) )
                return -EINVAL;

        /* Construct IA_NA sub-options list */
        suboptions.data = ia_na->options;
        suboptions.len = ( len + sizeof ( ia_na->header ) -
                           offsetof ( typeof ( *ia_na ), options ) );

        /* Check IA_NA status code */
        if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 )
                return rc;

        /* Find identity association address, if present */
        option = dhcpv6_option ( &suboptions, DHCPV6_IAADDR );
        if ( ! option )
                return -ENOENT;
        iaaddr = &option->iaaddr;

        /* Sanity check */
        len = ntohs ( iaaddr->header.len );
        if ( len < ( sizeof ( *iaaddr ) - sizeof ( iaaddr->header ) ) )
                return -EINVAL;

        /* Construct IAADDR sub-options list */
        suboptions.data = iaaddr->options;
        suboptions.len = ( len + sizeof ( iaaddr->header ) -
                           offsetof ( typeof ( *iaaddr ), options ) );

        /* Check IAADDR status code */
        if ( ( rc = dhcpv6_status_code ( &suboptions ) ) != 0 )
                return rc;

        /* Extract IPv6 address */
        memcpy ( address, &iaaddr->address, sizeof ( *address ) );

        return 0;
}
static int dhcpv6_applies ( struct settings *settings  __unused,
const struct setting setting 
) [static]

Check applicability of DHCPv6 setting.

Parameters:
settingsSettings block
settingSetting
Return values:
appliesSetting applies within this settings block

Definition at line 282 of file dhcpv6.c.

References dhcpv6_scope, setting::scope, and setting_cmp().

                                                            {

        return ( ( setting->scope == &dhcpv6_scope ) ||
                 ( setting_cmp ( setting, &ip6_setting ) == 0 ) );
}
static int dhcpv6_fetch_lease ( struct dhcpv6_settings dhcpv6set,
void *  data,
size_t  len 
) [static]

Fetch value of DHCPv6 leased address.

Parameters:
dhcpsetDHCPv6 settings
dataBuffer to fill with setting data
lenLength of buffer
Return values:
lenLength of setting data, or negative error

Definition at line 297 of file dhcpv6.c.

References ENOENT, IN6_IS_ADDR_UNSPECIFIED, lease, dhcpv6_settings::lease, and memcpy().

Referenced by dhcpv6_fetch().

                                                         {
        struct in6_addr *lease = &dhcpv6set->lease;

        /* Do nothing unless a leased address exists */
        if ( IN6_IS_ADDR_UNSPECIFIED ( lease ) )
                return -ENOENT;

        /* Copy leased address */
        if ( len > sizeof ( *lease ) )
                len = sizeof ( *lease );
        memcpy ( data, lease, len );

        return sizeof ( *lease );
}
static int dhcpv6_fetch ( struct settings settings,
struct setting setting,
void *  data,
size_t  len 
) [static]

Fetch value of DHCPv6 setting.

Parameters:
settingsSettings block
settingSetting to fetch
dataBuffer to fill with setting data
lenLength of buffer
Return values:
lenLength of setting data, or negative error

Definition at line 322 of file dhcpv6.c.

References container_of, dhcpv6_option::data, dhcpv6_fetch_lease(), dhcpv6_option(), ENOENT, dhcpv6_any_option::header, dhcpv6_option::len, memcpy(), ntohs, dhcpv6_settings::options, setting_cmp(), and setting::tag.

                                                   {
        struct dhcpv6_settings *dhcpv6set =
                container_of ( settings, struct dhcpv6_settings, settings );
        const union dhcpv6_any_option *option;
        size_t option_len;

        /* Handle leased address */
        if ( setting_cmp ( setting, &ip6_setting ) == 0 )
                return dhcpv6_fetch_lease ( dhcpv6set, data, len );

        /* Find option */
        option = dhcpv6_option ( &dhcpv6set->options, setting->tag );
        if ( ! option )
                return -ENOENT;

        /* Copy option to data buffer */
        option_len = ntohs ( option->header.len );
        if ( len > option_len )
                len = option_len;
        memcpy ( data, option->header.data, len );
        return option_len;
}
static int dhcpv6_register ( struct in6_addr lease,
struct dhcpv6_option_list options,
struct settings parent 
) [static]

Register DHCPv6 options as network device settings.

Parameters:
leaseDHCPv6 leased address
optionsDHCPv6 option list
parentParent settings block
Return values:
rcReturn status code

Definition at line 361 of file dhcpv6.c.

References dhcpv6_option_list::data, data, dhcpv6_scope, DHCPV6_SETTINGS_NAME, ENOMEM, IPV6_ORDER_DHCPV6, dhcpv6_settings::lease, dhcpv6_option_list::len, len, memcpy(), NULL, dhcpv6_settings::options, settings::order, rc, ref_init, ref_put, dhcpv6_settings::refcnt, register_settings(), dhcpv6_settings::settings, settings_init(), and zalloc().

Referenced by dhcpv6_rx().

                                                       {
        struct dhcpv6_settings *dhcpv6set;
        void *data;
        size_t len;
        int rc;

        /* Allocate and initialise structure */
        dhcpv6set = zalloc ( sizeof ( *dhcpv6set ) + options->len );
        if ( ! dhcpv6set ) {
                rc = -ENOMEM;
                goto err_alloc;
        }
        ref_init ( &dhcpv6set->refcnt, NULL );
        settings_init ( &dhcpv6set->settings, &dhcpv6_settings_operations,
                        &dhcpv6set->refcnt, &dhcpv6_scope );
        dhcpv6set->settings.order = IPV6_ORDER_DHCPV6;
        data = ( ( ( void * ) dhcpv6set ) + sizeof ( *dhcpv6set ) );
        len = options->len;
        memcpy ( data, options->data, len );
        dhcpv6set->options.data = data;
        dhcpv6set->options.len = len;
        memcpy ( &dhcpv6set->lease, lease, sizeof ( dhcpv6set->lease ) );

        /* Register settings */
        if ( ( rc = register_settings ( &dhcpv6set->settings, parent,
                                        DHCPV6_SETTINGS_NAME ) ) != 0 )
                goto err_register;

 err_register:
        ref_put ( &dhcpv6set->refcnt );
 err_alloc:
        return rc;
}
static const char* dhcpv6_type_name ( unsigned int  type) [static]

Name a DHCPv6 packet type.

Parameters:
typeDHCPv6 packet type
Return values:
nameDHCPv6 packet type name

Definition at line 428 of file dhcpv6.c.

References DHCPV6_ADVERTISE, DHCPV6_INFORMATION_REQUEST, DHCPV6_REPLY, DHCPV6_REQUEST, DHCPV6_SOLICIT, and snprintf().

Referenced by dhcpv6_rx(), and dhcpv6_set_state().

                                       {
        static char buf[ 12 /* "UNKNOWN-xxx" + NUL */ ];

        switch ( type ) {
        case DHCPV6_SOLICIT:                    return "SOLICIT";
        case DHCPV6_ADVERTISE:                  return "ADVERTISE";
        case DHCPV6_REQUEST:                    return "REQUEST";
        case DHCPV6_REPLY:                      return "REPLY";
        case DHCPV6_INFORMATION_REQUEST:        return "INFORMATION-REQUEST";
        default:
                snprintf ( buf, sizeof ( buf ), "UNKNOWN-%d", type );
                return buf;
        }
}
static void dhcpv6_free ( struct refcnt refcnt) [static]

Free DHCPv6 session.

Parameters:
refcntReference count

Definition at line 533 of file dhcpv6.c.

References container_of, free, dhcpv6_session::netdev, netdev_put(), and dhcpv6_session::server_duid.

Referenced by start_dhcpv6().

                                                  {
        struct dhcpv6_session *dhcpv6 =
                container_of ( refcnt, struct dhcpv6_session, refcnt );

        netdev_put ( dhcpv6->netdev );
        free ( dhcpv6->server_duid );
        free ( dhcpv6 );
}
static void dhcpv6_finished ( struct dhcpv6_session dhcpv6,
int  rc 
) [static]

Terminate DHCPv6 session.

Parameters:
dhcpv6DHCPv6 session
rcReason for close

Definition at line 548 of file dhcpv6.c.

References intf_shutdown(), dhcpv6_session::job, stop_timer(), dhcpv6_session::timer, and dhcpv6_session::xfer.

Referenced by dhcpv6_rx(), dhcpv6_timer_expired(), and start_dhcpv6().

                                                                      {

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

        /* Shut down interfaces */
        intf_shutdown ( &dhcpv6->xfer, rc );
        intf_shutdown ( &dhcpv6->job, rc );
}
static void dhcpv6_set_state ( struct dhcpv6_session dhcpv6,
struct dhcpv6_session_state state 
) [static]

Transition to new DHCPv6 session state.

Parameters:
dhcpv6DHCPv6 session
stateNew session state

Definition at line 564 of file dhcpv6.c.

References DBGC, dhcpv6_type_name(), ETIMEDOUT, net_device::name, dhcpv6_session::netdev, dhcpv6_session::rc, start_timer_nodelay(), state, dhcpv6_session::state, dhcpv6_session::timer, and dhcpv6_session_state::tx_type.

Referenced by dhcpv6_rx(), and start_dhcpv6().

                                                                    {

        DBGC ( dhcpv6, "DHCPv6 %s entering %s state\n", dhcpv6->netdev->name,
               dhcpv6_type_name ( state->tx_type ) );

        /* Record state */
        dhcpv6->state = state;

        /* Default to -ETIMEDOUT if no more specific error is recorded */
        dhcpv6->rc = -ETIMEDOUT;

        /* Start timer to trigger transmission */
        start_timer_nodelay ( &dhcpv6->timer );
}
static size_t dhcpv6_user_class ( void *  data,
size_t  len 
) [static]

Get DHCPv6 user class.

Parameters:
dataData buffer
lenLength of data buffer
Return values:
lenLength of user class

Definition at line 587 of file dhcpv6.c.

References fetch_raw_setting(), memcpy(), and NULL.

Referenced by dhcpv6_tx().

                                                           {
        static const char default_user_class[4] = { 'i', 'P', 'X', 'E' };
        int actual_len;

        /* Fetch user-class setting, if defined */
        actual_len = fetch_raw_setting ( NULL, &user_class_setting, data, len );
        if ( actual_len >= 0 )
                return actual_len;

        /* Otherwise, use the default user class ("iPXE") */
        if ( len > sizeof ( default_user_class ) )
                len = sizeof ( default_user_class );
        memcpy ( data, default_user_class, len );
        return sizeof ( default_user_class );
}
static int dhcpv6_tx ( struct dhcpv6_session dhcpv6) [static]

Transmit current request.

Parameters:
dhcpv6DHCPv6 session
Return values:
rcReturn status code

Definition at line 609 of file dhcpv6.c.

References dhcpv6_iaaddr_option::address, assert, dhcpv6_session::client_duid, dhcpv6_option::code, currticks(), DBGC, DHCPV6_CLIENT_ID, DHCPV6_ELAPSED_TIME, DHCPV6_IA_NA, DHCPV6_IAADDR, dhcpv6_request_options_data, DHCPV6_SERVER_ID, DHCPV6_TX_IA_NA, DHCPV6_TX_IAADDR, DHCPV6_USER_CLASS, dhcpv6_user_class(), dhcpv6_duid_option::duid, dhcpv6_elapsed_time_option::elapsed, ENOMEM, dhcpv6_session_state::flags, dhcpv6_duid_option::header, dhcpv6_ia_na_option::header, dhcpv6_iaaddr_option::header, dhcpv6_elapsed_time_option::header, dhcpv6_user_class_option::header, htonl, htons, dhcpv6_ia_na_option::iaid, dhcpv6_session::iaid, iob_len(), iob_put, dhcpv6_session::lease, dhcpv6_option::len, dhcpv6_user_class::len, memcpy(), net_device::name, dhcpv6_session::netdev, NULL, dhcpv6_ia_na_option::options, options, dhcpv6_iaaddr_option::preferred, rc, dhcpv6_ia_na_option::rebind, dhcpv6_ia_na_option::renew, dhcpv6_session::server_duid, dhcpv6_session::server_duid_len, dhcpv6_session::start, dhcpv6_session::state, strerror(), dhcpv6_user_class::string, TICKS_PER_SEC, dhcpv6_session_state::tx_type, dhcpv6_header::type, dhcpv6_user_class_option::user_class, dhcpv6_iaaddr_option::valid, dhcpv6_session::xfer, xfer_alloc_iob(), xfer_deliver_iob(), dhcpv6_header::xid, and dhcpv6_session::xid.

Referenced by dhcpv6_timer_expired().

                                                       {
        struct dhcpv6_duid_option *client_id;
        struct dhcpv6_duid_option *server_id;
        struct dhcpv6_ia_na_option *ia_na;
        struct dhcpv6_iaaddr_option *iaaddr;
        struct dhcpv6_user_class_option *user_class;
        struct dhcpv6_elapsed_time_option *elapsed;
        struct dhcpv6_header *dhcphdr;
        struct io_buffer *iobuf;
        void *options;
        size_t client_id_len;
        size_t server_id_len;
        size_t ia_na_len;
        size_t user_class_string_len;
        size_t user_class_len;
        size_t elapsed_len;
        size_t total_len;
        int rc;

        /* Calculate lengths */
        client_id_len = ( sizeof ( *client_id ) +
                          sizeof ( dhcpv6->client_duid ) );
        server_id_len = ( dhcpv6->server_duid ? ( sizeof ( *server_id ) +
                                                  dhcpv6->server_duid_len ) :0);
        if ( dhcpv6->state->flags & DHCPV6_TX_IA_NA ) {
                ia_na_len = sizeof ( *ia_na );
                if ( dhcpv6->state->flags & DHCPV6_TX_IAADDR )
                        ia_na_len += sizeof ( *iaaddr );
        } else {
                ia_na_len = 0;
        }
        user_class_string_len = dhcpv6_user_class ( NULL, 0 );
        user_class_len = ( sizeof ( *user_class ) +
                           sizeof ( user_class->user_class[0] ) +
                           user_class_string_len );
        elapsed_len = sizeof ( *elapsed );
        total_len = ( sizeof ( *dhcphdr ) + client_id_len + server_id_len +
                      ia_na_len + sizeof ( dhcpv6_request_options_data ) +
                      user_class_len + elapsed_len );

        /* Allocate packet */
        iobuf = xfer_alloc_iob ( &dhcpv6->xfer, total_len );
        if ( ! iobuf )
                return -ENOMEM;

        /* Construct header */
        dhcphdr = iob_put ( iobuf, sizeof ( *dhcphdr ) );
        dhcphdr->type = dhcpv6->state->tx_type;
        memcpy ( dhcphdr->xid, dhcpv6->xid, sizeof ( dhcphdr->xid ) );

        /* Construct client identifier */
        client_id = iob_put ( iobuf, client_id_len );
        client_id->header.code = htons ( DHCPV6_CLIENT_ID );
        client_id->header.len = htons ( client_id_len -
                                        sizeof ( client_id->header ) );
        memcpy ( client_id->duid, &dhcpv6->client_duid,
                 sizeof ( dhcpv6->client_duid ) );

        /* Construct server identifier, if applicable */
        if ( server_id_len ) {
                server_id = iob_put ( iobuf, server_id_len );
                server_id->header.code = htons ( DHCPV6_SERVER_ID );
                server_id->header.len = htons ( server_id_len -
                                                sizeof ( server_id->header ) );
                memcpy ( server_id->duid, dhcpv6->server_duid,
                         dhcpv6->server_duid_len );
        }

        /* Construct identity association, if applicable */
        if ( ia_na_len ) {
                ia_na = iob_put ( iobuf, ia_na_len );
                ia_na->header.code = htons ( DHCPV6_IA_NA );
                ia_na->header.len = htons ( ia_na_len -
                                            sizeof ( ia_na->header ) );
                ia_na->iaid = htonl ( dhcpv6->iaid );
                ia_na->renew = htonl ( 0 );
                ia_na->rebind = htonl ( 0 );
                if ( dhcpv6->state->flags & DHCPV6_TX_IAADDR ) {
                        iaaddr = ( ( void * ) ia_na->options );
                        iaaddr->header.code = htons ( DHCPV6_IAADDR );
                        iaaddr->header.len = htons ( sizeof ( *iaaddr ) -
                                                     sizeof ( iaaddr->header ));
                        memcpy ( &iaaddr->address, &dhcpv6->lease,
                                 sizeof ( iaaddr->address ) );
                        iaaddr->preferred = htonl ( 0 );
                        iaaddr->valid = htonl ( 0 );
                }
        }

        /* Construct fixed request options */
        options = iob_put ( iobuf, sizeof ( dhcpv6_request_options_data ) );
        memcpy ( options, dhcpv6_request_options_data,
                 sizeof ( dhcpv6_request_options_data ) );

        /* Construct user class */
        user_class = iob_put ( iobuf, user_class_len );
        user_class->header.code = htons ( DHCPV6_USER_CLASS );
        user_class->header.len = htons ( user_class_len -
                                         sizeof ( user_class->header ) );
        user_class->user_class[0].len = htons ( user_class_string_len );
        dhcpv6_user_class ( user_class->user_class[0].string,
                            user_class_string_len );

        /* Construct elapsed time */
        elapsed = iob_put ( iobuf, elapsed_len );
        elapsed->header.code = htons ( DHCPV6_ELAPSED_TIME );
        elapsed->header.len = htons ( elapsed_len -
                                      sizeof ( elapsed->header ) );
        elapsed->elapsed = htons ( ( ( currticks() - dhcpv6->start ) * 100 ) /
                                   TICKS_PER_SEC );

        /* Sanity check */
        assert ( iob_len ( iobuf ) == total_len );

        /* Transmit packet */
        if ( ( rc = xfer_deliver_iob ( &dhcpv6->xfer, iobuf ) ) != 0 ) {
                DBGC ( dhcpv6, "DHCPv6 %s could not transmit: %s\n",
                       dhcpv6->netdev->name, strerror ( rc ) );
                return rc;
        }

        return 0;
}
static void dhcpv6_timer_expired ( struct retry_timer timer,
int  fail 
) [static]

Handle timer expiry.

Parameters:
timerRetransmission timer
failFailure indicator

Definition at line 739 of file dhcpv6.c.

References container_of, dhcpv6_finished(), dhcpv6_tx(), dhcpv6_session::rc, start_timer(), and dhcpv6_session::timer.

Referenced by start_dhcpv6().

                                                                         {
        struct dhcpv6_session *dhcpv6 =
                container_of ( timer, struct dhcpv6_session, timer );

        /* If we have failed, terminate DHCPv6 */
        if ( fail ) {
                dhcpv6_finished ( dhcpv6, dhcpv6->rc );
                return;
        }

        /* Restart timer */
        start_timer ( &dhcpv6->timer );

        /* (Re)transmit current request */
        dhcpv6_tx ( dhcpv6 );
}
static int dhcpv6_rx ( struct dhcpv6_session dhcpv6,
struct io_buffer iobuf,
struct xfer_metadata meta 
) [static]

Receive new data.

Parameters:
dhcpv6DHCPv6 session
iobufI/O buffer
metaData transfer metadata
Return values:
rcReturn status code

Definition at line 764 of file dhcpv6.c.

References AF_INET6, assert, dhcpv6_session::client_duid, io_buffer::data, dhcpv6_option_list::data, DBGC, dhcpv6_check_duid(), DHCPV6_CLIENT_ID, dhcpv6_finished(), dhcpv6_iaaddr(), dhcpv6_option(), dhcpv6_register(), DHCPV6_RX_RECORD_IAADDR, DHCPV6_RX_RECORD_SERVER_ID, DHCPV6_SERVER_ID, dhcpv6_set_state(), dhcpv6_status_code(), dhcpv6_type_name(), done, dhcpv6_duid_option::duid, dhcpv6_any_option::duid, EINVAL, ENOMEM, ENOTTY, dhcpv6_session_state::flags, free_iob(), dhcpv6_duid_option::header, dhcpv6_session::iaid, inet6_ntoa(), iob_len(), dhcpv6_session::lease, dhcpv6_option::len, dhcpv6_option_list::len, malloc(), memcpy(), net_device::name, dhcpv6_session::netdev, netdev_settings(), dhcpv6_session_state::next, ntohs, NULL, offsetof, dhcpv6_header::options, settings::parent, rc, dhcpv6_session::rc, dhcpv6_session_state::rx_type, dhcpv6_session::server_duid, dhcpv6_session::server_duid_len, sockaddr_in6::sin6_addr, sockaddr_in6::sin6_family, xfer_metadata::src, src, dhcpv6_session::state, strerror(), and dhcpv6_header::type.

                                                    {
        struct settings *parent = netdev_settings ( dhcpv6->netdev );
        struct sockaddr_in6 *src = ( ( struct sockaddr_in6 * ) meta->src );
        struct dhcpv6_header *dhcphdr = iobuf->data;
        struct dhcpv6_option_list options;
        const union dhcpv6_any_option *option;
        int rc;

        /* Sanity checks */
        if ( iob_len ( iobuf ) < sizeof ( *dhcphdr ) ) {
                DBGC ( dhcpv6, "DHCPv6 %s received packet too short (%zd "
                       "bytes, min %zd bytes)\n", dhcpv6->netdev->name,
                       iob_len ( iobuf ), sizeof ( *dhcphdr ) );
                rc = -EINVAL;
                goto done;
        }
        assert ( src != NULL );
        assert ( src->sin6_family == AF_INET6 );
        DBGC ( dhcpv6, "DHCPv6 %s received %s from %s\n",
               dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ),
               inet6_ntoa ( &src->sin6_addr ) );

        /* Construct option list */
        options.data = dhcphdr->options;
        options.len = ( iob_len ( iobuf ) -
                        offsetof ( typeof ( *dhcphdr ), options ) );

        /* Verify client identifier */
        if ( ( rc = dhcpv6_check_duid ( &options, DHCPV6_CLIENT_ID,
                                        &dhcpv6->client_duid,
                                        sizeof ( dhcpv6->client_duid ) ) ) !=0){
                DBGC ( dhcpv6, "DHCPv6 %s received %s without correct client "
                       "ID: %s\n", dhcpv6->netdev->name,
                       dhcpv6_type_name ( dhcphdr->type ), strerror ( rc ) );
                goto done;
        }

        /* Verify server identifier, if applicable */
        if ( dhcpv6->server_duid &&
             ( ( rc = dhcpv6_check_duid ( &options, DHCPV6_SERVER_ID,
                                          dhcpv6->server_duid,
                                          dhcpv6->server_duid_len ) ) != 0 ) ) {
                DBGC ( dhcpv6, "DHCPv6 %s received %s without correct server "
                       "ID: %s\n", dhcpv6->netdev->name,
                       dhcpv6_type_name ( dhcphdr->type ), strerror ( rc ) );
                goto done;
        }

        /* Check message type */
        if ( dhcphdr->type != dhcpv6->state->rx_type ) {
                DBGC ( dhcpv6, "DHCPv6 %s received %s while expecting %s\n",
                       dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ),
                       dhcpv6_type_name ( dhcpv6->state->rx_type ) );
                rc = -ENOTTY;
                goto done;
        }

        /* Fetch status code, if present */
        if ( ( rc = dhcpv6_status_code ( &options ) ) != 0 ) {
                DBGC ( dhcpv6, "DHCPv6 %s received %s with error status: %s\n",
                       dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ),
                       strerror ( rc ) );
                /* This is plausibly the error we want to return */
                dhcpv6->rc = rc;
                goto done;
        }

        /* Record identity association address, if applicable */
        if ( dhcpv6->state->flags & DHCPV6_RX_RECORD_IAADDR ) {
                if ( ( rc = dhcpv6_iaaddr ( &options, dhcpv6->iaid,
                                            &dhcpv6->lease ) ) != 0 ) {
                        DBGC ( dhcpv6, "DHCPv6 %s received %s with unusable "
                               "IAADDR: %s\n", dhcpv6->netdev->name,
                               dhcpv6_type_name ( dhcphdr->type ),
                               strerror ( rc ) );
                        /* This is plausibly the error we want to return */
                        dhcpv6->rc = rc;
                        goto done;
                }
                DBGC ( dhcpv6, "DHCPv6 %s received %s is for %s\n",
                       dhcpv6->netdev->name, dhcpv6_type_name ( dhcphdr->type ),
                       inet6_ntoa ( &dhcpv6->lease ) );
        }

        /* Record server ID, if applicable */
        if ( dhcpv6->state->flags & DHCPV6_RX_RECORD_SERVER_ID ) {
                assert ( dhcpv6->server_duid == NULL );
                option = dhcpv6_option ( &options, DHCPV6_SERVER_ID );
                if ( ! option ) {
                        DBGC ( dhcpv6, "DHCPv6 %s received %s missing server "
                               "ID\n", dhcpv6->netdev->name,
                               dhcpv6_type_name ( dhcphdr->type ) );
                        rc = -EINVAL;
                        goto done;
                }
                dhcpv6->server_duid_len = ntohs ( option->duid.header.len );
                dhcpv6->server_duid = malloc ( dhcpv6->server_duid_len );
                if ( ! dhcpv6->server_duid ) {
                        rc = -ENOMEM;
                        goto done;
                }
                memcpy ( dhcpv6->server_duid, option->duid.duid,
                         dhcpv6->server_duid_len );
        }

        /* Transition to next state, if applicable */
        if ( dhcpv6->state->next ) {
                dhcpv6_set_state ( dhcpv6, dhcpv6->state->next );
                rc = 0;
                goto done;
        }

        /* Register settings */
        if ( ( rc = dhcpv6_register ( &dhcpv6->lease, &options,
                                      parent ) ) != 0 ) {
                DBGC ( dhcpv6, "DHCPv6 %s could not register settings: %s\n",
                       dhcpv6->netdev->name, strerror ( rc ) );
                goto done;
        }

        /* Mark as complete */
        dhcpv6_finished ( dhcpv6, 0 );
        DBGC ( dhcpv6, "DHCPv6 %s complete\n", dhcpv6->netdev->name );

 done:
        free_iob ( iobuf );
        return rc;
}
int start_dhcpv6 ( struct interface job,
struct net_device netdev,
int  stateful 
)

Start DHCPv6.

Parameters:
jobJob control interface
netdevNetwork device
statefulPerform stateful address autoconfiguration
Return values:
rcReturn status code

Definition at line 921 of file dhcpv6.c.

References AF_INET6, dhcpv6_session::client_duid, crc32_le(), currticks(), DBGC, DHCPV6_CLIENT_PORT, DHCPV6_DUID_UUID, dhcpv6_finished(), dhcpv6_free(), DHCPV6_SERVER_PORT, dhcpv6_set_state(), dhcpv6_timer_expired(), ENOMEM, fetch_uuid_setting(), htons, dhcpv6_session::iaid, net_device::index, intf_init(), intf_plug_plug(), ipv6_all_dhcp_relay_and_servers(), dhcpv6_session::job, len, net_device::ll_addr, ll_protocol::ll_addr_len, net_device::ll_protocol, memcpy(), memset(), net_device::name, dhcpv6_session::netdev, netdev_get(), NULL, random(), rc, ref_init, ref_put, dhcpv6_session::refcnt, SOCK_DGRAM, dhcpv6_session::start, strerror(), dhcpv6_session::timer, dhcpv6_duid_uuid::type, dhcpv6_duid_uuid::uuid, dhcpv6_session::xfer, xfer_open_socket(), dhcpv6_session::xid, and zalloc().

Referenced by ipv6conf_rx_router_advertisement().

                                  {
        struct ll_protocol *ll_protocol = netdev->ll_protocol;
        struct dhcpv6_session *dhcpv6;
        struct {
                union {
                        struct sockaddr_in6 sin6;
                        struct sockaddr sa;
                } client;
                union {
                        struct sockaddr_in6 sin6;
                        struct sockaddr sa;
                } server;
        } addresses;
        uint32_t xid;
        int len;
        int rc;

        /* Allocate and initialise structure */
        dhcpv6 = zalloc ( sizeof ( *dhcpv6 ) );
        if ( ! dhcpv6 )
                return -ENOMEM;
        ref_init ( &dhcpv6->refcnt, dhcpv6_free );
        intf_init ( &dhcpv6->job, &dhcpv6_job_desc, &dhcpv6->refcnt );
        intf_init ( &dhcpv6->xfer, &dhcpv6_xfer_desc, &dhcpv6->refcnt );
        dhcpv6->netdev = netdev_get ( netdev );
        xid = random();
        memcpy ( dhcpv6->xid, &xid, sizeof ( dhcpv6->xid ) );
        dhcpv6->start = currticks();
        timer_init ( &dhcpv6->timer, dhcpv6_timer_expired, &dhcpv6->refcnt );

        /* Construct client and server addresses */
        memset ( &addresses, 0, sizeof ( addresses ) );
        addresses.client.sin6.sin6_family = AF_INET6;
        addresses.client.sin6.sin6_port = htons ( DHCPV6_CLIENT_PORT );
        addresses.server.sin6.sin6_family = AF_INET6;
        ipv6_all_dhcp_relay_and_servers ( &addresses.server.sin6.sin6_addr );
        addresses.server.sin6.sin6_scope_id = netdev->index;
        addresses.server.sin6.sin6_port = htons ( DHCPV6_SERVER_PORT );

        /* Construct client DUID from system UUID */
        dhcpv6->client_duid.type = htons ( DHCPV6_DUID_UUID );
        if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting,
                                          &dhcpv6->client_duid.uuid ) ) < 0 ) {
                rc = len;
                DBGC ( dhcpv6, "DHCPv6 %s could not create DUID-UUID: %s\n",
                       dhcpv6->netdev->name, strerror ( rc ) );
                goto err_client_duid;
        }

        /* Construct IAID from link-layer address */
        dhcpv6->iaid = crc32_le ( 0, netdev->ll_addr, ll_protocol->ll_addr_len);
        DBGC ( dhcpv6, "DHCPv6 %s has XID %02x%02x%02x\n", dhcpv6->netdev->name,
               dhcpv6->xid[0], dhcpv6->xid[1], dhcpv6->xid[2] );

        /* Enter initial state */
        dhcpv6_set_state ( dhcpv6, ( stateful ? &dhcpv6_solicit :
                                     &dhcpv6_information_request ) );

        /* Open socket */
        if ( ( rc = xfer_open_socket ( &dhcpv6->xfer, SOCK_DGRAM,
                                       &addresses.server.sa,
                                       &addresses.client.sa ) ) != 0 ) {
                DBGC ( dhcpv6, "DHCPv6 %s could not open socket: %s\n",
                       dhcpv6->netdev->name, strerror ( rc ) );
                goto err_open_socket;
        }

        /* Attach parent interface, mortalise self, and return */
        intf_plug_plug ( &dhcpv6->job, job );
        ref_put ( &dhcpv6->refcnt );
        return 0;

 err_open_socket:
        dhcpv6_finished ( dhcpv6, rc );
 err_client_duid:
        ref_put ( &dhcpv6->refcnt );
        return rc;
}
struct setting filename6_setting __setting ( SETTING_BOOT  ,
filename   
) [read]

Boot filename setting.

struct setting dnssl6_setting __setting ( SETTING_IP_EXTRA  ,
dnssl   
) [read]

DNS search list setting.


Variable Documentation

struct errortab dhcpv6_errors [] __errortab
Initial value:

Human-readable error messages.

Definition at line 74 of file dhcpv6.c.

Initial value:
 {
        .applies = dhcpv6_applies,
        .fetch = dhcpv6_fetch,
}

DHCPv6 settings operations.

Definition at line 348 of file dhcpv6.c.

Initial value:

DHCPv6 request state.

Definition at line 468 of file dhcpv6.c.

Initial value:

DHCPv6 solicitation state.

Definition at line 477 of file dhcpv6.c.

Initial value:
 {
        .tx_type = DHCPV6_INFORMATION_REQUEST,
        .rx_type = DHCPV6_REPLY,
        .flags = 0,
        .next = NULL,
}

DHCPv6 information request state.

Definition at line 486 of file dhcpv6.c.

Initial value:

DHCPv6 job control interface operations.

Definition at line 896 of file dhcpv6.c.

Initial value:

DHCPv6 job control interface descriptor.

Definition at line 901 of file dhcpv6.c.

Initial value:

DHCPv6 data transfer interface operations.

Definition at line 905 of file dhcpv6.c.

Initial value:

DHCPv6 data transfer interface descriptor.

Definition at line 910 of file dhcpv6.c.