dhcp: Add DHCP discover sending
On starting the client, use the supplied interface mac address and create a transaction id. Puzzle together an IP/UDP/DHCP Discover message, compute checksums and send it out as a raw packet. Create an additional function that constructs default options common to all DHCP messages. Set the DHCP Client ID option as noticed by Grant Erickson in ConnMan commit b18d9798b3a0ae46ed87d6d2be8d5a474bf3ab1e: "Some Internet gateways and Wi-Fi access points are unhappy when the DHCPv4 client-id option (61) is missing and will refuse to issue a DHCP lease."
This commit is contained in:
parent
8b4a96932d
commit
46a66b794a
|
@ -21,19 +21,25 @@
|
|||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <net/ethernet.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "list.h"
|
||||
|
||||
#include "dhcp-protocol.h"
|
||||
#include "dhcp-internal.h"
|
||||
#include "sd-dhcp-client.h"
|
||||
|
||||
#define DHCP_CLIENT_MIN_OPTIONS_SIZE 312
|
||||
|
||||
struct sd_dhcp_client {
|
||||
DHCPState state;
|
||||
int index;
|
||||
uint8_t *req_opts;
|
||||
size_t req_opts_size;
|
||||
uint32_t last_addr;
|
||||
struct ether_addr mac_addr;
|
||||
uint32_t xid;
|
||||
};
|
||||
|
||||
static const uint8_t default_req_opts[] = {
|
||||
|
@ -102,6 +108,157 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_client_set_mac(sd_dhcp_client *client,
|
||||
const struct ether_addr *addr)
|
||||
{
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(client->state == DHCP_STATE_INIT, -EBUSY);
|
||||
|
||||
memcpy(&client->mac_addr, addr, ETH_ALEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int client_packet_init(sd_dhcp_client *client, uint8_t type,
|
||||
DHCPMessage *message, uint8_t **opt,
|
||||
size_t *optlen)
|
||||
{
|
||||
int err;
|
||||
|
||||
*opt = (uint8_t *)(message + 1);
|
||||
|
||||
if (*optlen < 4)
|
||||
return -ENOBUFS;
|
||||
*optlen -= 4;
|
||||
|
||||
message->op = BOOTREQUEST;
|
||||
message->htype = 1;
|
||||
message->hlen = ETHER_ADDR_LEN;
|
||||
message->xid = htobe32(client->xid);
|
||||
|
||||
memcpy(&message->chaddr, &client->mac_addr, ETH_ALEN);
|
||||
(*opt)[0] = 0x63;
|
||||
(*opt)[1] = 0x82;
|
||||
(*opt)[2] = 0x53;
|
||||
(*opt)[3] = 0x63;
|
||||
|
||||
*opt += 4;
|
||||
|
||||
err = dhcp_option_append(opt, optlen, DHCP_OPTION_MESSAGE_TYPE, 1,
|
||||
&type);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Some DHCP servers will refuse to issue an DHCP lease if the Cliient
|
||||
Identifier option is not set */
|
||||
err = dhcp_option_append(opt, optlen, DHCP_OPTION_CLIENT_IDENTIFIER,
|
||||
ETH_ALEN, &client->mac_addr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (type == DHCP_DISCOVER || type == DHCP_REQUEST) {
|
||||
err = dhcp_option_append(opt, optlen,
|
||||
DHCP_OPTION_PARAMETER_REQUEST_LIST,
|
||||
client->req_opts_size,
|
||||
client->req_opts);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint16_t client_checksum(void *buf, int len)
|
||||
{
|
||||
uint32_t sum;
|
||||
uint16_t *check;
|
||||
int i;
|
||||
uint8_t *odd;
|
||||
|
||||
sum = 0;
|
||||
check = buf;
|
||||
|
||||
for (i = 0; i < len / 2 ; i++)
|
||||
sum += check[i];
|
||||
|
||||
if (len & 0x01) {
|
||||
odd = buf;
|
||||
sum += odd[len];
|
||||
}
|
||||
|
||||
return ~((sum & 0xffff) + (sum >> 16));
|
||||
}
|
||||
|
||||
static int client_send_discover(sd_dhcp_client *client)
|
||||
{
|
||||
int err = 0;
|
||||
_cleanup_free_ DHCPPacket *discover;
|
||||
size_t optlen, len;
|
||||
uint8_t *opt;
|
||||
|
||||
optlen = DHCP_CLIENT_MIN_OPTIONS_SIZE;
|
||||
len = sizeof(DHCPPacket) + optlen;
|
||||
|
||||
discover = malloc0(len);
|
||||
|
||||
if (!discover)
|
||||
return -ENOMEM;
|
||||
|
||||
err = client_packet_init(client, DHCP_DISCOVER, &discover->dhcp,
|
||||
&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;
|
||||
|
||||
discover->ip.version = IPVERSION;
|
||||
discover->ip.ihl = sizeof(discover->ip) >> 2;
|
||||
discover->ip.tot_len = htobe16(len);
|
||||
|
||||
discover->ip.protocol = IPPROTO_UDP;
|
||||
discover->ip.saddr = INADDR_ANY;
|
||||
discover->ip.daddr = INADDR_BROADCAST;
|
||||
|
||||
discover->udp.source = htobe16(DHCP_PORT_CLIENT);
|
||||
discover->udp.dest = htobe16(DHCP_PORT_SERVER);
|
||||
discover->udp.len = htobe16(len - sizeof(discover->ip));
|
||||
|
||||
discover->ip.check = discover->udp.len;
|
||||
discover->udp.check = client_checksum(&discover->ip.ttl,
|
||||
len - 8);
|
||||
|
||||
discover->ip.ttl = IPDEFTTL;
|
||||
discover->ip.check = 0;
|
||||
discover->ip.check = client_checksum(&discover->ip,
|
||||
sizeof(discover->ip));
|
||||
|
||||
err = dhcp_network_send_raw_packet(client->index, discover, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_dhcp_client_start(sd_dhcp_client *client)
|
||||
{
|
||||
assert_return(client, -EINVAL);
|
||||
assert_return(client->index >= 0, -EINVAL);
|
||||
assert_return(client->state == DHCP_STATE_INIT ||
|
||||
client->state == DHCP_STATE_INIT_REBOOT, -EBUSY);
|
||||
|
||||
client->xid = random_u();
|
||||
|
||||
return client_send_discover(client);
|
||||
}
|
||||
|
||||
sd_dhcp_client *sd_dhcp_client_new(void)
|
||||
{
|
||||
sd_dhcp_client *client;
|
||||
|
|
|
@ -55,6 +55,11 @@ struct DHCPPacket {
|
|||
|
||||
typedef struct DHCPPacket DHCPPacket;
|
||||
|
||||
enum {
|
||||
DHCP_PORT_SERVER = 67,
|
||||
DHCP_PORT_CLIENT = 68,
|
||||
};
|
||||
|
||||
enum DHCPState {
|
||||
DHCP_STATE_INIT = 0,
|
||||
DHCP_STATE_SELECTING = 1,
|
||||
|
@ -100,5 +105,6 @@ enum {
|
|||
DHCP_OPTION_OVERLOAD = 52,
|
||||
DHCP_OPTION_MESSAGE_TYPE = 53,
|
||||
DHCP_OPTION_PARAMETER_REQUEST_LIST = 55,
|
||||
DHCP_OPTION_CLIENT_IDENTIFIER = 61,
|
||||
DHCP_OPTION_END = 255,
|
||||
};
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
***/
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <net/ethernet.h>
|
||||
|
||||
typedef struct sd_dhcp_client sd_dhcp_client;
|
||||
|
||||
|
@ -30,7 +31,10 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option);
|
|||
int sd_dhcp_client_set_request_address(sd_dhcp_client *client,
|
||||
const struct in_addr *last_address);
|
||||
int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index);
|
||||
int sd_dhcp_client_set_mac(sd_dhcp_client *client,
|
||||
const struct ether_addr *addr);
|
||||
|
||||
int sd_dhcp_client_start(sd_dhcp_client *client);
|
||||
sd_dhcp_client *sd_dhcp_client_new(void);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue