Beispiel #1
0
// decline a
static err_t dhcp_decline(struct dhcp_state *state)
{
  err_t result = ERR_OK;
  u16_t msecs;
  DEBUGF(DHCP_DEBUG, ("dhcp_decline()\n"));
  dhcp_set_state(state, DHCP_BACKING_OFF);
  // create and initialize the DHCP message header
  result = dhcp_create_request(state);
  if (result == ERR_OK)
  {
    dhcp_option(state, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
    dhcp_option_byte(state, DHCP_DECLINE);

    dhcp_option(state, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
    dhcp_option_short(state, 576);

    dhcp_option_trailer(state);
    // resize pbuf to reflect true size of options
    pbuf_realloc(state->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + state->options_out_len);

    udp_bind(state->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
    udp_connect(state->pcb, &state->server_ip_addr, DHCP_SERVER_PORT);
    udp_send(state->pcb, state->p_out);
    dhcp_delete_request(state);
  }
  state->tries++;
  msecs = 10*1000;
  state->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
  DEBUGF(DHCP_DEBUG, ("dhcp_decline(): request timeout %u msecs\n", msecs));
  return result;
}
Beispiel #2
0
void dhcp_inform(struct netif *netif)
{
  struct dhcp_state *state = NULL;
  err_t result = ERR_OK;
  state = mem_malloc(sizeof(struct dhcp_state));
  if (state == NULL)
  {
    DEBUGF(DHCP_DEBUG, ("dhcp_inform(): could not allocate dhcp_state\n"));
    return;
  }  
  memset(state, 0, sizeof(struct dhcp_state));

  DEBUGF(DHCP_DEBUG, ("dhcp_inform(): allocated dhcp_state\n"));
  state->pcb = udp_new();
  if (state->pcb == NULL) {
    DEBUGF(DHCP_DEBUG, ("dhcp_inform(): could not obtain pcb\n"));
    mem_free((void *)state);
    return;
  }
  DEBUGF(DHCP_DEBUG, ("dhcp_inform(): created new udp pcb\n"));
  state->netif = netif;
  // we are last in list 
  state->next = NULL;
  // create and initialize the DHCP message header
  result = dhcp_create_request(state);
  if (result == ERR_OK)
  {

    dhcp_option(state, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
    dhcp_option_byte(state, DHCP_INFORM);

    dhcp_option(state, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
    dhcp_option_short(state, 576);

    dhcp_option_trailer(state);

    pbuf_realloc(state->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + state->options_out_len);

    udp_bind(state->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
    udp_connect(state->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT);
    udp_send(state->pcb, state->p_out);
    udp_connect(state->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
    dhcp_delete_request(state);
  }

  if (state != NULL)
  {
    if (state->pcb != NULL) udp_remove(state->pcb);
    state->pcb = NULL;
    mem_free((void *)state);
  }
}
Beispiel #3
0
/**
 * Start the DHCP process, discover a server
 *
 */
static err_t dhcp_discover(struct dhcp_state *state)
{
  err_t result = ERR_OK;
  u16_t msecs;
  DEBUGF(DHCP_DEBUG, ("dhcp_discover(%p)\n", state));
  ip_addr_set(&state->offered_ip_addr, IP_ADDR_ANY);
  // create and initialize the DHCP message header
  DEBUGF(DHCP_DEBUG, ("set ip addr, creating request()\n"));

  result = dhcp_create_request(state);
  DEBUGF(DHCP_DEBUG, ("created request\n"));
  
  if (result == ERR_OK)
  {
    dhcp_option(state, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
    dhcp_option_byte(state, DHCP_DISCOVER);

    dhcp_option(state, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
    dhcp_option_short(state, 576);

    dhcp_option(state, DHCP_OPTION_PARAMETER_REQUEST_LIST, 3);
    dhcp_option_byte(state, DHCP_OPTION_SUBNET_MASK);
    dhcp_option_byte(state, DHCP_OPTION_ROUTER);
    dhcp_option_byte(state, DHCP_OPTION_BROADCAST);

    dhcp_option_trailer(state);

    pbuf_realloc(state->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + state->options_out_len);
    DEBUGF(DHCP_DEBUG, ("about to do udp recv\n"));
    udp_recv(state->pcb, dhcp_recv, state);
    DEBUGF(DHCP_DEBUG, ("about to do udp bind\n"));
    udp_bind(state->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
    DEBUGF(DHCP_DEBUG, ("about to do udp connect\n"));
    udp_connect(state->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT);

    DEBUGF(DHCP_DEBUG, ("about to do udp send\n"));
    udp_send(state->pcb, state->p_out);
    udp_bind(state->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
    udp_connect(state->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
    dhcp_delete_request(state);
  }
  state->tries++;
  msecs = state->tries < 4 ? (state->tries + 1) * 1000 : 10 * 1000;
  state->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
  DEBUGF(DHCP_DEBUG, ("dhcp_discover(): request timeout %u msecs\n", msecs));
  dhcp_set_state(state, DHCP_SELECTING);
  return result;
}
Beispiel #4
0
static err_t dhcp_release(struct dhcp_state *state)
{
  err_t result;
  u16_t msecs;
  DEBUGF(DHCP_DEBUG, ("dhcp_release()\n"));
  // and idle DHCP client
  dhcp_set_state(state, DHCP_OFF);

  // create and initialize the DHCP message header
  result = dhcp_create_request(state);
  if (result == ERR_OK)
  {
    dhcp_option(state, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
    dhcp_option_byte(state, DHCP_RELEASE);

    dhcp_option_trailer(state);

    pbuf_realloc(state->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + state->options_out_len);

    udp_bind(state->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
    udp_connect(state->pcb, &state->server_ip_addr, DHCP_SERVER_PORT);
    udp_send(state->pcb, state->p_out);
    dhcp_delete_request(state);
  }
  state->tries++;
  msecs = state->tries < 10 ? state->tries * 1000 : 10 * 1000;
  state->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
  DEBUGF(DHCP_DEBUG, ("dhcp_release(): request timeout %u msecs\n", msecs));
  // remove IP address from interface
  netif_set_ipaddr(state->netif, IP_ADDR_ANY);
  netif_set_gw(state->netif, IP_ADDR_ANY);
  netif_set_netmask(state->netif, IP_ADDR_ANY);
  return result;
}
Beispiel #5
0
/**
 * Select a DHCP server offer out of all offers.
 *
 * Simply select the first offer received, ignore others
 */
static err_t dhcp_select(struct dhcp_state *state)
{
  err_t result;
  u32_t msecs;
  DEBUGF(DHCP_DEBUG, ("dhcp_select()\n"));

  // create and initialize the DHCP message header
  result = dhcp_create_request(state);
  if (result == ERR_OK)
  {
    dhcp_option(state, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
    dhcp_option_byte(state, DHCP_REQUEST);

    dhcp_option(state, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
    dhcp_option_short(state, 576);

    /* MUST request the offered IP address */
    dhcp_option(state, DHCP_OPTION_REQUESTED_IP, 4);
    dhcp_option_long(state, ntohl(state->offered_ip_addr.addr));

    dhcp_option(state, DHCP_OPTION_SERVER_ID, 4);
    dhcp_option_long(state, ntohl(state->server_ip_addr.addr));

    dhcp_option(state, DHCP_OPTION_PARAMETER_REQUEST_LIST, 3);
    dhcp_option_byte(state, DHCP_OPTION_SUBNET_MASK);
    dhcp_option_byte(state, DHCP_OPTION_ROUTER);
    dhcp_option_byte(state, DHCP_OPTION_BROADCAST);

    dhcp_option_trailer(state);

    pbuf_realloc(state->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + state->options_out_len);

    udp_bind(state->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
    udp_connect(state->pcb, IP_ADDR_BROADCAST, DHCP_SERVER_PORT);
    udp_send(state->pcb, state->p_out);
    // reconnect to any (or to server here?!)
    udp_connect(state->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
    dhcp_delete_request(state);
  }
  state->tries++;
  msecs = state->tries < 4 ? state->tries * 1000 : 4 * 1000;
  state->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
  DEBUGF(DHCP_DEBUG, ("dhcp_select(): request timeout %u msecs\n", msecs));
  dhcp_set_state(state, DHCP_REQUESTING);
  return result;
}
Beispiel #6
0
/**
 * Find DHCP option within DHCP options block, and its encapsulator (if any)
 *
 * @v options		DHCP options block
 * @v tag		DHCP option tag to search for
 * @ret encap_offset	Offset of encapsulating DHCP option
 * @ret offset		Offset of DHCP option, or negative error
 *
 * Searches for the DHCP option matching the specified tag within the
 * DHCP option block.  Encapsulated options may be searched for by
 * using DHCP_ENCAP_OPT() to construct the tag value.
 *
 * If the option is encapsulated, and @c encap_offset is non-NULL, it
 * will be filled in with the offset of the encapsulating option.
 *
 * This routine is designed to be paranoid.  It does not assume that
 * the option data is well-formatted, and so must guard against flaws
 * such as options missing a @c DHCP_END terminator, or options whose
 * length would take them beyond the end of the data block.
 */
static int find_dhcp_option_with_encap ( struct dhcp_options *options,
        unsigned int tag,
        int *encap_offset ) {
    unsigned int original_tag __attribute__ (( unused )) = tag;
    struct dhcp_option *option;
    int offset = 0;
    ssize_t remaining = options->used_len;
    unsigned int option_len;

    /* Sanity check */
    if ( tag == DHCP_PAD )
        return -ENOENT;

    /* Search for option */
    while ( remaining ) {
        /* Calculate length of this option.  Abort processing
         * if the length is malformed (i.e. takes us beyond
         * the end of the data block).
         */
        option = dhcp_option ( options, offset );
        option_len = dhcp_option_len ( option );
        remaining -= option_len;
        if ( remaining < 0 )
            break;
        /* Check for explicit end marker */
        if ( option->tag == DHCP_END ) {
            if ( tag == DHCP_END )
                /* Special case where the caller is interested
                 * in whether we have this marker or not.
                 */
                return offset;
            else
                break;
        }
        /* Check for matching tag */
        if ( option->tag == tag ) {
            DBGC ( options, "DHCPOPT %p found %s (length %d)\n",
                   options, dhcp_tag_name ( original_tag ),
                   option_len );
            return offset;
        }
        /* Check for start of matching encapsulation block */
        if ( DHCP_IS_ENCAP_OPT ( tag ) &&
                ( option->tag == DHCP_ENCAPSULATOR ( tag ) ) ) {
            if ( encap_offset )
                *encap_offset = offset;
            /* Continue search within encapsulated option block */
            tag = DHCP_ENCAPSULATED ( tag );
            remaining = option_len;
            offset += DHCP_OPTION_HEADER_LEN;
            continue;
        }
        offset += option_len;
    }

    return -ENOENT;
}
Beispiel #7
0
err_t dhcp_renew(struct dhcp_state *state)
{
  err_t result;
  u16_t msecs;
  DEBUGF(DHCP_DEBUG, ("dhcp_renew()\n"));
  dhcp_set_state(state, DHCP_RENEWING);

  // create and initialize the DHCP message header
  result = dhcp_create_request(state);
  if (result == ERR_OK)
  {

    dhcp_option(state, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
    dhcp_option_byte(state, DHCP_REQUEST);

    dhcp_option(state, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
    dhcp_option_short(state, 576);

#if 0
    dhcp_option(state, DHCP_OPTION_REQUESTED_IP, 4);
    dhcp_option_long(state, ntohl(state->offered_ip_addr.addr));
#endif

#if 0
    dhcp_option(state, DHCP_OPTION_SERVER_ID, 4);
    dhcp_option_long(state, ntohl(state->server_ip_addr.addr));
#endif

    dhcp_option_trailer(state);

    pbuf_realloc(state->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + state->options_out_len);

    udp_bind(state->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
    udp_connect(state->pcb, &state->server_ip_addr, DHCP_SERVER_PORT);
    udp_send(state->pcb, state->p_out);
    dhcp_delete_request(state);
  }
  state->tries++;
  // back-off on retries, but to a maximum of 20 seconds
  msecs = state->tries < 10 ? state->tries * 2000 : 20 * 1000;
  state->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
  DEBUGF(DHCP_DEBUG, ("dhcp_renew(): request timeout %u msecs\n", msecs));
  return result;
}
Beispiel #8
0
/**
 * Fetch value of DHCP option setting
 *
 * @v options		DHCP option block
 * @v tag		Setting tag number
 * @v data		Buffer to fill with setting data
 * @v len		Length of buffer
 * @ret len		Length of setting data, or negative error
 */
int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
                    void *data, size_t len ) {
    int offset;
    struct dhcp_option *option;
    size_t option_len;

    offset = find_dhcp_option_with_encap ( options, tag, NULL );
    if ( offset < 0 )
        return offset;

    option = dhcp_option ( options, offset );
    option_len = option->len;
    if ( len > option_len )
        len = option_len;
    memcpy ( data, option->data, len );

    return option_len;
}
Beispiel #9
0
/**
 * Recalculate length of DHCP options block
 *
 * @v options		Uninitialised DHCP option block
 *
 * The "used length" field will be updated based on scanning through
 * the block to find the end of the options.
 */
void dhcpopt_update_used_len ( struct dhcp_options *options ) {
    struct dhcp_option *option;
    int offset = 0;
    ssize_t remaining = options->alloc_len;
    unsigned int option_len;

    /* Find last non-pad option */
    options->used_len = 0;
    while ( remaining ) {
        option = dhcp_option ( options, offset );
        option_len = dhcp_option_len ( option );
        remaining -= option_len;
        if ( remaining < 0 )
            break;
        offset += option_len;
        if ( option->tag != DHCP_PAD )
            options->used_len = offset;
    }
}
Beispiel #10
0
/**
 * Set value of DHCP option
 *
 * @v options		DHCP option block
 * @v tag		DHCP option tag
 * @v data		New value for DHCP option
 * @v len		Length of value, in bytes
 * @ret offset		Offset of DHCP option, or negative error
 *
 * Sets the value of a DHCP option within the options block.  The
 * option may or may not already exist.  Encapsulators will be created
 * (and deleted) as necessary.
 *
 * This call may fail due to insufficient space in the options block.
 * If it does fail, and the option existed previously, the option will
 * be left with its original value.
 */
static int set_dhcp_option ( struct dhcp_options *options, unsigned int tag,
                             const void *data, size_t len ) {
    static const uint8_t empty_encap[] = { DHCP_END };
    int offset;
    int encap_offset = -1;
    int creation_offset;
    struct dhcp_option *option;
    unsigned int encap_tag = DHCP_ENCAPSULATOR ( tag );
    size_t old_len = 0;
    size_t new_len = ( len ? ( len + DHCP_OPTION_HEADER_LEN ) : 0 );
    int rc;

    /* Sanity check */
    if ( tag == DHCP_PAD )
        return -ENOTTY;

    creation_offset = find_dhcp_option_with_encap ( options, DHCP_END,
                      NULL );
    if ( creation_offset < 0 )
        creation_offset = options->used_len;
    /* Find old instance of this option, if any */
    offset = find_dhcp_option_with_encap ( options, tag, &encap_offset );
    if ( offset >= 0 ) {
        old_len = dhcp_option_len ( dhcp_option ( options, offset ) );
        DBGC ( options, "DHCPOPT %p resizing %s from %zd to %zd\n",
               options, dhcp_tag_name ( tag ), old_len, new_len );
    } else {
        DBGC ( options, "DHCPOPT %p creating %s (length %zd)\n",
               options, dhcp_tag_name ( tag ), new_len );
    }

    /* Ensure that encapsulator exists, if required */
    if ( encap_tag ) {
        if ( encap_offset < 0 ) {
            encap_offset =
                set_dhcp_option ( options, encap_tag,
                                  empty_encap,
                                  sizeof ( empty_encap ) );
        }
        if ( encap_offset < 0 )
            return encap_offset;
        creation_offset = ( encap_offset + DHCP_OPTION_HEADER_LEN );
    }

    /* Create new option if necessary */
    if ( offset < 0 )
        offset = creation_offset;

    /* Resize option to fit new data */
    if ( ( rc = resize_dhcp_option ( options, offset, encap_offset,
                                     old_len, new_len ) ) != 0 )
        return rc;

    /* Copy new data into option, if applicable */
    if ( len ) {
        option = dhcp_option ( options, offset );
        option->tag = tag;
        option->len = len;
        memcpy ( &option->data, data, len );
    }

    /* Delete encapsulator if there's nothing else left in it */
    if ( encap_offset >= 0 ) {
        option = dhcp_option ( options, encap_offset );
        if ( option->len <= 1 )
            set_dhcp_option ( options, encap_tag, NULL, 0 );
    }

    return offset;
}
Beispiel #11
0
/**
 * Resize a DHCP option
 *
 * @v options		DHCP option block
 * @v offset		Offset of option to resize
 * @v encap_offset	Offset of encapsulating offset (or -ve for none)
 * @v old_len		Old length (including header)
 * @v new_len		New length (including header)
 * @ret rc		Return status code
 */
static int resize_dhcp_option ( struct dhcp_options *options,
                                int offset, int encap_offset,
                                size_t old_len, size_t new_len ) {
    struct dhcp_option *encapsulator;
    struct dhcp_option *option;
    ssize_t delta = ( new_len - old_len );
    size_t old_alloc_len;
    size_t new_used_len;
    size_t new_encapsulator_len;
    void *source;
    void *dest;
    int rc;

    /* Check for sufficient space */
    if ( new_len > DHCP_MAX_LEN ) {
        DBGC ( options, "DHCPOPT %p overlength option\n", options );
        return -ENOSPC;
    }
    new_used_len = ( options->used_len + delta );

    /* Expand options block, if necessary */
    if ( new_used_len > options->alloc_len ) {
        /* Reallocate options block */
        old_alloc_len = options->alloc_len;
        if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ) {
            DBGC ( options, "DHCPOPT %p could not reallocate to "
                   "%zd bytes\n", options, new_used_len );
            return rc;
        }
        /* Clear newly allocated space */
        memset ( ( options->data + old_alloc_len ), 0,
                 ( options->alloc_len - old_alloc_len ) );
    }

    /* Update encapsulator, if applicable */
    if ( encap_offset >= 0 ) {
        encapsulator = dhcp_option ( options, encap_offset );
        new_encapsulator_len = ( encapsulator->len + delta );
        if ( new_encapsulator_len > DHCP_MAX_LEN ) {
            DBGC ( options, "DHCPOPT %p overlength encapsulator\n",
                   options );
            return -ENOSPC;
        }
        encapsulator->len = new_encapsulator_len;
    }

    /* Update used length */
    options->used_len = new_used_len;

    /* Move remainder of option data */
    option = dhcp_option ( options, offset );
    source = ( ( ( void * ) option ) + old_len );
    dest = ( ( ( void * ) option ) + new_len );
    memmove ( dest, source, ( new_used_len - offset - new_len ) );

    /* Shrink options block, if applicable */
    if ( new_used_len < options->alloc_len ) {
        if ( ( rc = options->realloc ( options, new_used_len ) ) != 0 ) {
            DBGC ( options, "DHCPOPT %p could not reallocate to "
                   "%zd bytes\n", options, new_used_len );
            return rc;
        }
    }

    return 0;
}