Beispiel #1
0
static int client_send_discover(sd_dhcp_client *client) {
        _cleanup_free_ DHCPPacket *discover = NULL;
        size_t optoffset, optlen;
        usec_t time_now;
        int r;

        assert(client);
        assert(client->state == DHCP_STATE_INIT ||
               client->state == DHCP_STATE_SELECTING);

        /* See RFC2131 section 4.4.1 */

        r = sd_event_now(client->event, CLOCK_MONOTONIC, &time_now);
        if (r < 0)
                return r;
        assert(time_now >= client->start_time);

        /* seconds between sending first and last DISCOVER
         * must always be strictly positive to deal with broken servers */
        client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;

        r = client_message_init(client, &discover, DHCP_DISCOVER,
                                &optlen, &optoffset);
        if (r < 0)
                return r;

        /* the client may suggest values for the network address
           and lease time in the DHCPDISCOVER message. The client may include
           the ’requested IP address’ option to suggest that a particular IP
           address be assigned, and may include the ’IP address lease time’
           option to suggest the lease time it would like.
         */
        if (client->last_addr != INADDR_ANY) {
                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
                                       DHCP_OPTION_REQUESTED_IP_ADDRESS,
                                       4, &client->last_addr);
                if (r < 0)
                        return r;
        }

        r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
                               DHCP_OPTION_END, 0, NULL);

        /* We currently ignore:
           The client SHOULD wait a random time between one and ten seconds to
           desynchronize the use of DHCP at startup.
         */
        r = dhcp_client_send_raw(client, discover, sizeof(DHCPPacket) + optoffset);
        if (r < 0)
                return r;

        log_dhcp_client(client, "DISCOVER");

        return 0;
}
static void test_option_set(void)
{
        size_t len, oldlen;
        int pos, i;
        uint8_t *opt;

        assert_se(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);

        len = 0;
        opt = &result[0];
        assert_se(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
        assert_se(opt == &result[0] && len == 0);

        assert_se(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
                                  0, NULL) == -ENOBUFS);
        assert_se(opt == &result[0] && len == 0);

        opt = &result[4];
        len = 1;
        assert_se(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
                                    0, NULL) >= 0);
        assert_se(opt == &result[5] && len == 0);

        pos = 4;
        len = 60;
        while (pos < 64 && options[pos] != DHCP_OPTION_END) {
                opt = &result[pos];
                oldlen = len;

                assert_se(dhcp_option_append(&opt, &len, options[pos],
                                          options[pos + 1],
                                          &options[pos + 2]) >= 0);

                if (options[pos] == DHCP_OPTION_PAD) {
                        assert_se(opt == &result[pos + 1]);
                        assert_se(len == oldlen - 1);
                        pos++;
                } else {
                        assert_se(opt == &result[pos + 2 + options[pos + 1]]);
                        assert_se(len == oldlen - 2 - options[pos + 1]);
                        pos += 2 + options[pos + 1];
                }
        }

        for (i = 0; i < pos; i++) {
                if (verbose)
                        printf("%2d: 0x%02x(0x%02x)\n", i, result[i],
                               options[i]);
                assert_se(result[i] == options[i]);
        }

        if (verbose)
                printf ("\n");
}
static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
                               uint8_t type, uint16_t secs, uint8_t **opt,
                               size_t *optlen) {
        int r;

        assert(secs);

        r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
                              optlen);
        if (r < 0)
                return r;

        /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
           refuse to issue an DHCP lease if 'secs' is set to zero */
        message->secs = htobe16(secs);

        memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);

        if (client->state == DHCP_STATE_RENEWING ||
            client->state == DHCP_STATE_REBINDING)
                message->ciaddr = client->lease->address;

        /* Some DHCP servers will refuse to issue an DHCP lease if the Client
           Identifier option is not set */
        r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
                               ETH_ALEN, &client->mac_addr);
        if (r < 0)
                return r;

        if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
                be16_t max_size;

                r = dhcp_option_append(opt, optlen,
                                       DHCP_OPTION_PARAMETER_REQUEST_LIST,
                                       client->req_opts_size,
                                       client->req_opts);
                if (r < 0)
                        return r;

                /* Some DHCP servers will send bigger DHCP packets than the
                   defined default size unless the Maximum Messge Size option
                   is explicitely set */
                max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
                                   DHCP_MIN_OPTIONS_SIZE);
                r = dhcp_option_append(opt, optlen,
                                       DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
                                       2, &max_size);
                if (r < 0)
                        return r;
        }

        return 0;
}
static int client_send_request(sd_dhcp_client *client, uint16_t secs) {
        _cleanup_free_ DHCPPacket *request;
        size_t optlen, len;
        int err;
        uint8_t *opt;

        optlen = DHCP_MIN_OPTIONS_SIZE;
        len = sizeof(DHCPPacket) + optlen;

        request = malloc0(len);
        if (!request)
                return -ENOMEM;

        err = client_message_init(client, &request->dhcp, DHCP_REQUEST, secs,
                                  &opt, &optlen);
        if (err < 0)
                return err;

        if (client->state == DHCP_STATE_REQUESTING) {
                err = dhcp_option_append(&opt, &optlen,
                                         DHCP_OPTION_REQUESTED_IP_ADDRESS,
                                         4, &client->lease->address);
                if (err < 0)
                        return err;

                err = dhcp_option_append(&opt, &optlen,
                                         DHCP_OPTION_SERVER_IDENTIFIER,
                                         4, &client->lease->server_address);
                if (err < 0)
                        return err;
        }

        err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
        if (err < 0)
                return err;

        if (client->state == DHCP_STATE_RENEWING) {
                err = dhcp_network_send_udp_socket(client->fd,
                                                   client->lease->server_address,
                                                   DHCP_PORT_SERVER,
                                                   &request->dhcp,
                                                   len - DHCP_IP_UDP_SIZE);
        } else {
                err = dhcp_client_send_raw(client, request, len);
        }
        if (err < 0)
                return err;

        log_dhcp_client(client, "REQUEST");

        return 0;
}
static int client_send_discover(sd_dhcp_client *client, uint16_t secs) {
        int err = 0;
        _cleanup_free_ DHCPPacket *discover;
        size_t optlen, len;
        uint8_t *opt;

        optlen = DHCP_MIN_OPTIONS_SIZE;
        len = sizeof(DHCPPacket) + optlen;

        discover = malloc0(len);

        if (!discover)
                return -ENOMEM;

        err = client_message_init(client, &discover->dhcp, DHCP_DISCOVER,
                                  secs, &opt, &optlen);
        if (err < 0)
                return err;

        if (client->last_addr != INADDR_ANY) {
                err = dhcp_option_append(&opt, &optlen,
                                         DHCP_OPTION_REQUESTED_IP_ADDRESS,
                                         4, &client->last_addr);
                if (err < 0)
                        return err;
        }

        err = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
        if (err < 0)
                return err;

        err = dhcp_client_send_raw(client, discover, len);
        if (err < 0)
                return err;

        log_dhcp_client(client, "DISCOVER");

        return 0;
}
Beispiel #6
0
static int client_send_request(sd_dhcp_client *client) {
        _cleanup_free_ DHCPPacket *request;
        size_t optlen, len;
        uint8_t *opt;
        int r;

        optlen = DHCP_MIN_OPTIONS_SIZE;
        len = sizeof(DHCPPacket) + optlen;

        request = malloc0(len);
        if (!request)
                return -ENOMEM;

        r = client_message_init(client, &request->dhcp, DHCP_REQUEST, &opt,
                                &optlen);
        if (r < 0)
                return r;

        switch (client->state) {
        /* See RFC2131 section 4.3.2 (note that there is a typo in the RFC,
           SELECTING should be REQUESTING)
         */

        case DHCP_STATE_REQUESTING:
                /* Client inserts the address of the selected server in ’server
                   identifier’, ’ciaddr’ MUST be zero, ’requested IP address’ MUST be
                   filled in with the yiaddr value from the chosen DHCPOFFER.
                 */

                r = dhcp_option_append(&opt, &optlen,
                                       DHCP_OPTION_SERVER_IDENTIFIER,
                                       4, &client->lease->server_address);
                if (r < 0)
                        return r;

                r = dhcp_option_append(&opt, &optlen,
                                       DHCP_OPTION_REQUESTED_IP_ADDRESS,
                                       4, &client->lease->address);
                if (r < 0)
                        return r;

                break;

        case DHCP_STATE_INIT_REBOOT:
                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
                   option MUST be filled in with client’s notion of its previously
                   assigned address. ’ciaddr’ MUST be zero.
                 */
                r = dhcp_option_append(&opt, &optlen,
                                       DHCP_OPTION_REQUESTED_IP_ADDRESS,
                                       4, &client->last_addr);
                if (r < 0)
                        return r;
                break;

        case DHCP_STATE_RENEWING:
                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
                   option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
                   client’s IP address.
                */

                /* fall through */
        case DHCP_STATE_REBINDING:
                /* ’server identifier’ MUST NOT be filled in, ’requested IP address’
                   option MUST NOT be filled in, ’ciaddr’ MUST be filled in with
                   client’s IP address.

                   This message MUST be broadcast to the 0xffffffff IP broadcast address.
                 */
                request->dhcp.ciaddr = client->lease->address;

                break;

        case DHCP_STATE_INIT:
        case DHCP_STATE_SELECTING:
        case DHCP_STATE_REBOOTING:
        case DHCP_STATE_BOUND:
        case DHCP_STATE_STOPPED:
                return -EINVAL;
        }

        r = dhcp_option_append(&opt, &optlen, DHCP_OPTION_END, 0, NULL);
        if (r < 0)
                return r;

        if (client->state == DHCP_STATE_RENEWING) {
                r = dhcp_network_send_udp_socket(client->fd,
                                                 client->lease->server_address,
                                                 DHCP_PORT_SERVER,
                                                 &request->dhcp,
                                                 len - optlen - DHCP_IP_UDP_SIZE);
        } else {
                r = dhcp_client_send_raw(client, request, len - optlen);
        }
        if (r < 0)
                return r;

        switch (client->state) {
        case DHCP_STATE_REQUESTING:
                log_dhcp_client(client, "REQUEST (requesting)");
                break;
        case DHCP_STATE_INIT_REBOOT:
                log_dhcp_client(client, "REQUEST (init-reboot)");
                break;
        case DHCP_STATE_RENEWING:
                log_dhcp_client(client, "REQUEST (renewing)");
                break;
        case DHCP_STATE_REBINDING:
                log_dhcp_client(client, "REQUEST (rebinding)");
                break;
        default:
                log_dhcp_client(client, "REQUEST (invalid)");
                break;
        }

        return 0;
}
Beispiel #7
0
static int client_message_init(sd_dhcp_client *client, DHCPMessage *message,
                               uint8_t type, uint8_t **opt, size_t *optlen) {
        be16_t max_size;
        int r;

        assert(client);
        assert(client->secs);
        assert(message);
        assert(opt);
        assert(optlen);
        assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);

        r = dhcp_message_init(message, BOOTREQUEST, client->xid, type, opt,
                              optlen);
        if (r < 0)
                return r;

        /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
           refuse to issue an DHCP lease if 'secs' is set to zero */
        message->secs = htobe16(client->secs);

        /* RFC2132 section 4.1.1:
           The client MUST include its hardware address in the ’chaddr’ field, if
           necessary for delivery of DHCP reply messages.
         */
        memcpy(&message->chaddr, &client->client_id.mac_addr, ETH_ALEN);

        /* Some DHCP servers will refuse to issue an DHCP lease if the Client
           Identifier option is not set */
        r = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
                               sizeof(client->client_id), &client->client_id);
        if (r < 0)
                return r;


        /* RFC2131 section 3.5:
           in its initial DHCPDISCOVER or DHCPREQUEST message, a
           client may provide the server with a list of specific
           parameters the client is interested in. If the client
           includes a list of parameters in a DHCPDISCOVER message,
           it MUST include that list in any subsequent DHCPREQUEST
           messages.
         */
        r = dhcp_option_append(opt, optlen,
                               DHCP_OPTION_PARAMETER_REQUEST_LIST,
                               client->req_opts_size,
                               client->req_opts);
        if (r < 0)
                return r;

        /* RFC2131 section 3.5:
           The client SHOULD include the ’maximum DHCP message size’ option to
           let the server know how large the server may make its DHCP messages.

           Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
           than the defined default size unless the Maximum Messge Size option
           is explicitely set
         */
        max_size = htobe16(DHCP_IP_UDP_SIZE + DHCP_MESSAGE_SIZE +
                           DHCP_MIN_OPTIONS_SIZE);
        r = dhcp_option_append(opt, optlen,
                               DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
                               2, &max_size);
        if (r < 0)
                return r;

        return 0;
}
Beispiel #8
0
static void test_option_set(void)
{
        _cleanup_free_ DHCPMessage *result = NULL;
        size_t offset = 0, len, pos;
        unsigned i;

        result = malloc0(sizeof(DHCPMessage) + 11);
        assert_se(result);

        result->options[0] = 'A';
        result->options[1] = 'B';
        result->options[2] = 'C';
        result->options[3] = 'D';

        assert_se(dhcp_option_append(result, 0, &offset, 0, DHCP_OPTION_PAD,
                                     0, NULL) == -ENOBUFS);
        assert_se(offset == 0);

        offset = 4;
        assert_se(dhcp_option_append(result, 5, &offset, 0, DHCP_OPTION_PAD,
                                     0, NULL) == -ENOBUFS);
        assert_se(offset == 4);
        assert_se(dhcp_option_append(result, 6, &offset, 0, DHCP_OPTION_PAD,
                                     0, NULL) >= 0);
        assert_se(offset == 5);

        offset = pos = 4;
        len = 11;
        while (pos < len && options[pos] != DHCP_OPTION_END) {
                assert_se(dhcp_option_append(result, len, &offset, DHCP_OVERLOAD_SNAME,
                                             options[pos],
                                             options[pos + 1],
                                             &options[pos + 2]) >= 0);

                if (options[pos] == DHCP_OPTION_PAD)
                        pos++;
                else
                        pos += 2 + options[pos + 1];

                if (pos < len)
                        assert_se(offset == pos);
        }

        for (i = 0; i < 9; i++) {
                if (verbose)
                        printf("%2d: 0x%02x(0x%02x) (options)\n", i, result->options[i],
                               options[i]);
                assert_se(result->options[i] == options[i]);
        }

        if (verbose)
                printf("%2d: 0x%02x(0x%02x) (options)\n", 9, result->options[9],
                       DHCP_OPTION_END);

        assert_se(result->options[9] == DHCP_OPTION_END);

        if (verbose)
                printf("%2d: 0x%02x(0x%02x) (options)\n", 10, result->options[10],
                       DHCP_OPTION_PAD);

        assert_se(result->options[10] == DHCP_OPTION_PAD);

        for (i = 0; i < pos - 8; i++) {
                if (verbose)
                        printf("%2d: 0x%02x(0x%02x) (sname)\n", i, result->sname[i],
                               options[i + 9]);
                assert_se(result->sname[i] == options[i + 9]);
        }

        if (verbose)
                printf ("\n");
}
Beispiel #9
0
static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
                               uint8_t type, size_t *_optlen, size_t *_optoffset) {
        _cleanup_free_ DHCPPacket *packet;
        size_t optlen, optoffset, size;
        be16_t max_size;
        int r;

        assert(client);
        assert(client->secs);
        assert(ret);
        assert(_optlen);
        assert(_optoffset);
        assert(type == DHCP_DISCOVER || type == DHCP_REQUEST);

        optlen = DHCP_MIN_OPTIONS_SIZE;
        size = sizeof(DHCPPacket) + optlen;

        packet = malloc0(size);
        if (!packet)
                return -ENOMEM;

        r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
                              optlen, &optoffset);
        if (r < 0)
                return r;

        /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers
           refuse to issue an DHCP lease if 'secs' is set to zero */
        packet->dhcp.secs = htobe16(client->secs);

        /* RFC2132 section 4.1
           A client that cannot receive unicast IP datagrams until its protocol
           software has been configured with an IP address SHOULD set the
           BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
           DHCPREQUEST messages that client sends.  The BROADCAST bit will
           provide a hint to the DHCP server and BOOTP relay agent to broadcast
           any messages to the client on the client's subnet. */
        if (client->broadcast)
                packet->dhcp.flags = htobe16(0x8000);

        /* RFC2132 section 4.1.1:
           The client MUST include its hardware address in the ’chaddr’ field, if
           necessary for delivery of DHCP reply messages.
         */
        memcpy(&packet->dhcp.chaddr, &client->client_id.mac_addr, ETH_ALEN);

        /* Some DHCP servers will refuse to issue an DHCP lease if the Client
           Identifier option is not set */
        r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
                               DHCP_OPTION_CLIENT_IDENTIFIER,
                               sizeof(client->client_id), &client->client_id);
        if (r < 0)
                return r;


        /* RFC2131 section 3.5:
           in its initial DHCPDISCOVER or DHCPREQUEST message, a
           client may provide the server with a list of specific
           parameters the client is interested in. If the client
           includes a list of parameters in a DHCPDISCOVER message,
           it MUST include that list in any subsequent DHCPREQUEST
           messages.
         */
        r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
                               DHCP_OPTION_PARAMETER_REQUEST_LIST,
                               client->req_opts_size, client->req_opts);
        if (r < 0)
                return r;

        /* RFC2131 section 3.5:
           The client SHOULD include the ’maximum DHCP message size’ option to
           let the server know how large the server may make its DHCP messages.

           Note (from ConnMan): Some DHCP servers will send bigger DHCP packets
           than the defined default size unless the Maximum Messge Size option
           is explicitely set
         */
        max_size = htobe16(size);
        r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
                               DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
                               2, &max_size);
        if (r < 0)
                return r;

        *_optlen = optlen;
        *_optoffset = optoffset;
        *ret = packet;
        packet = NULL;

        return 0;
}