[systemd-devel] [PATCH 11/17] sd-dhcp-server: add basic REQUEST/ACK support
Tom Gundersen
teg at jklm.no
Tue May 27 08:51:26 PDT 2014
On Tue, May 27, 2014 at 5:48 PM, Zbigniew Jędrzejewski-Szmek
<zbyszek at in.waw.pl> wrote:
> On Mon, May 26, 2014 at 09:39:40PM +0200, Tom Gundersen wrote:
>> ---
>> src/libsystemd-network/dhcp-server-internal.h | 3 +
>> src/libsystemd-network/sd-dhcp-server.c | 135 +++++++++++++++++++++++++-
>> src/libsystemd-network/test-dhcp-server.c | 37 +++++++
>> src/systemd/sd-dhcp-server.h | 1 +
>> 4 files changed, 171 insertions(+), 5 deletions(-)
>>
>> diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h
>> index 381304e..cd480e7 100644
>> --- a/src/libsystemd-network/dhcp-server-internal.h
>> +++ b/src/libsystemd-network/dhcp-server-internal.h
>> @@ -40,6 +40,8 @@ struct sd_dhcp_server {
>>
>> int index;
>> be32_t address;
>> + be32_t pool_start;
>> + size_t pool_size;
>> };
>>
>> typedef struct DHCPClientId {
>> @@ -55,6 +57,7 @@ typedef struct DHCPRequest {
>> DHCPClientId client_id;
>> size_t max_optlen;
>> be32_t server_id;
>> + be32_t requested_ip;
>> } DHCPRequest;
>>
>> DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_server*, sd_dhcp_server_unref);
>> diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
>> index be6938b..01cd8be 100644
>> --- a/src/libsystemd-network/sd-dhcp-server.c
>> +++ b/src/libsystemd-network/sd-dhcp-server.c
>> @@ -29,6 +29,21 @@
>>
>> #define DHCP_DEFAULT_LEASE_TIME 60
>>
>> +int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *address,
>> + size_t size) {
>> + assert_return(server, -EINVAL);
>> + assert_return(address, -EINVAL);
>> + assert_return(address->s_addr, -EINVAL);
>> + assert_return(size, -EINVAL);
>> + assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY);
>> + assert_return(!server->pool_size, -EBUSY);
>> +
>> + server->pool_start = address->s_addr;
>> + server->pool_size = size;
>> +
>> + return 0;
>> +}
>> +
>> int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address) {
>> assert_return(server, -EINVAL);
>> assert_return(address, -EINVAL);
>> @@ -288,7 +303,7 @@ static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
>> assert(server);
>> assert(ret);
>> assert(_optoffset);
>> - assert(type == DHCP_OFFER);
>> + assert(IN_SET(type, DHCP_OFFER, DHCP_ACK));
>>
>> packet = malloc0(sizeof(DHCPPacket) + req->max_optlen);
>> if (!packet)
>> @@ -310,7 +325,7 @@ static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret,
>> return 0;
>> }
>>
>> -static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req) {
>> +static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
>> _cleanup_free_ DHCPPacket *packet = NULL;
>> size_t offset;
>> be32_t lease_time;
>> @@ -320,8 +335,7 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req) {
>> if (r < 0)
>> return r;
>>
>> - /* for now offer a random IP */
>> - packet->dhcp.yiaddr = random_u32();
>> + packet->dhcp.yiaddr = address;
>>
>> /* for ten seconds */
>> lease_time = htobe32(DHCP_DEFAULT_LEASE_TIME);
>> @@ -337,6 +351,32 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req) {
>> return 0;
>> }
>>
>> +static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, be32_t address) {
>> + _cleanup_free_ DHCPPacket *packet = NULL;
>> + size_t offset;
>> + be32_t lease_time;
>> + int r;
>> +
>> + r = server_message_init(server, &packet, DHCP_ACK, &offset, req);
>> + if (r < 0)
>> + return r;
>> +
>> + packet->dhcp.yiaddr = address;
>> +
>> + /* for ten seconds */
>> + lease_time = htobe32(DHCP_DEFAULT_LEASE_TIME);
>> + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
>> + DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4, &lease_time);
>> + if (r < 0)
>> + return r;
>> +
>> + r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset);
>> + if (r < 0)
>> + return r;
>> +
>> + return 0;
>> +}
>> +
>> static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
>> void *user_data) {
>> DHCPRequest *req = user_data;
>> @@ -344,6 +384,11 @@ static int parse_request(uint8_t code, uint8_t len, const uint8_t *option,
>> assert(req);
>>
>> switch(code) {
>> + case DHCP_OPTION_REQUESTED_IP_ADDRESS:
>> + if (len == 4)
>> + req->requested_ip = *(be32_t*)option;
>> +
>> + break;
>> case DHCP_OPTION_SERVER_IDENTIFIER:
>> if (len == 4)
>> req->server_id = *(be32_t*)option;
>> @@ -439,10 +484,21 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
>>
>> switch(type) {
>> case DHCP_DISCOVER:
>> + {
>> + be32_t address;
>> +
>> log_dhcp_server(server, "DISCOVER (0x%x)",
>> be32toh(req->message->xid));
>>
>> - r = server_send_offer(server, req);
>> + if (!server->pool_size)
>> + /* no pool allocated */
>> + return 0;
>> +
>> + /* for now pick a random address from the pool */
>> + address = htobe32(be32toh(server->pool_start) +
>> + (random_u32() % server->pool_size));
>> +
>> + r = server_send_offer(server, req, address);
>> if (r < 0) {
>> log_dhcp_server(server, "could not send offer: %s",
>> strerror(-r));
>> @@ -455,6 +511,75 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
>>
>> break;
>> }
>> + case DHCP_REQUEST:
>> + {
>> + be32_t address;
>> +
>> + /* see RFC 2131, section 4.3.2 */
>> +
>> + if (req->server_id) {
>> + log_dhcp_server(server, "REQUEST (selecting) (0x%x)",
>> + be32toh(req->message->xid));
>> +
>> + /* SELECTING */
>> + if (req->server_id != server->address)
>> + /* client did not pick us */
>> + return 0;
>> +
>> + if (req->message->ciaddr)
>> + /* this MUST be zero */
>> + return 0;
>> +
>> + if (!req->requested_ip)
>> + /* this must be filled in with the yiaddr
>> + from the chosen OFFER */
>> + return 0;
>> +
>> + address = req->requested_ip;
>> + } else if (req->requested_ip) {
>> + log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)",
>> + be32toh(req->message->xid));
>> +
>> + /* INIT-REBOOT */
>> + if (req->message->ciaddr)
>> + /* this MUST be zero */
>> + return 0;
>> +
>> + /* TODO: check if requested IP is correct, NAK if not */
>> + address = req->requested_ip;
>> + } else {
>> + log_dhcp_server(server, "REQUEST (rebinding/renewing) (0x%x)",
>> + be32toh(req->message->xid));
>> +
>> + /* REBINDING / RENEWING */
>> + if (!req->message->ciaddr)
>> + /* this MUST be filled in with clients IP address */
>> + return 0;
>> +
>> + address = req->message->ciaddr;
>> + }
>> +
>> + /* for now we just verify that the address is from the pool, not
>> + whether or not it is taken */
>> + if (htobe32(req->requested_ip) >= htobe32(server->pool_start) &&
>> + htobe32(req->requested_ip) < htobe32(server->pool_start) +
>> + + server->pool_size) {
>> + r = server_send_ack(server, req, address);
>> + if (r < 0) {
>> + log_dhcp_server(server, "could not send ack: %s",
>> + strerror(-r));
>> + return 0;
>> + } else {
>> + log_dhcp_server(server, "ACK (0x%x)",
>> + be32toh(req->message->xid));
>> + return DHCP_ACK;
>> + }
>> + } else
>> + return 0;
>> +
>> + break;
>> + }
>> + }
> Should this closing brace be here? (hard to see in the diff format)
Yeah, that's correct. These two close:
switch(type) {
case DHCP_DISCOVER:
{
from above. Probably I could improve the indentation here...
-t
>> return 0;
>> }
>> diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c
>> index ed3aaf9..7255a69 100644
>> --- a/src/libsystemd-network/test-dhcp-server.c
>> +++ b/src/libsystemd-network/test-dhcp-server.c
>> @@ -59,6 +59,11 @@ static void test_basic(sd_event *event) {
>> assert_se(sd_dhcp_server_set_address(server, &address_lo) >= 0);
>> assert_se(sd_dhcp_server_set_address(server, &address_lo) == -EBUSY);
>>
>> + assert_se(sd_dhcp_server_set_lease_pool(server, &address_any, 1) == -EINVAL);
>> + assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 0) == -EINVAL);
>> + assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) >= 0);
>> + assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) == -EBUSY);
>> +
>> assert_se(sd_dhcp_server_start(server) >= 0);
>> assert_se(sd_dhcp_server_start(server) == -EBUSY);
>> assert_se(sd_dhcp_server_stop(server) >= 0);
>> @@ -75,11 +80,23 @@ static void test_message_handler(void) {
>> uint8_t length;
>> uint8_t type;
>> } _packed_ option_type;
>> + struct {
>> + uint8_t code;
>> + uint8_t length;
>> + be32_t address;
>> + } _packed_ option_requested_ip;
>> + struct {
>> + uint8_t code;
>> + uint8_t length;
>> + be32_t address;
>> + } _packed_ option_server_id;
>> uint8_t end;
>> } _packed_ test = {
>> .message.op = BOOTREQUEST,
>> .message.htype = ARPHRD_ETHER,
>> .message.hlen = ETHER_ADDR_LEN,
>> + .message.xid = htobe32(0x12345678),
>> + .message.chaddr = { 'A', 'B', 'C', 'D', 'E', 'F' },
>> .option_type.code = DHCP_OPTION_MESSAGE_TYPE,
>> .option_type.length = 1,
>> .option_type.type = DHCP_DISCOVER,
>> @@ -94,6 +111,8 @@ static void test_message_handler(void) {
>> assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0);
>> assert_se(sd_dhcp_server_start(server) >= 0);
>>
>> + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
>> + assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 10) >= 0);
>> assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
>>
>> test.end = 0;
>> @@ -125,6 +144,24 @@ static void test_message_handler(void) {
>> assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
>> test.message.hlen = ETHER_ADDR_LEN;
>> assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER);
>> +
>> + test.option_type.type = DHCP_REQUEST;
>> + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
>> + test.option_requested_ip.code = DHCP_OPTION_REQUESTED_IP_ADDRESS;
>> + test.option_requested_ip.length = 4;
>> + test.option_requested_ip.address = htobe32(0x12345678);
>> + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
>> + test.option_server_id.code = DHCP_OPTION_SERVER_IDENTIFIER;
>> + test.option_server_id.length = 4;
>> + test.option_server_id.address = htobe32(INADDR_LOOPBACK);
>> + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
>> + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK);
>> + test.option_server_id.address = htobe32(0x12345678);
>> + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 3);
>> + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
>> + test.option_server_id.address = htobe32(INADDR_LOOPBACK + 3);
>> + test.option_requested_ip.address = htobe32(0x12345678);
>> + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0);
>> }
>>
>> int main(int argc, char *argv[]) {
>> diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h
>> index 5edeffc..cd0ff72 100644
>> --- a/src/systemd/sd-dhcp-server.h
>> +++ b/src/systemd/sd-dhcp-server.h
>> @@ -42,4 +42,5 @@ int sd_dhcp_server_start(sd_dhcp_server *server);
>> int sd_dhcp_server_stop(sd_dhcp_server *server);
>>
>> int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address);
>> +int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *start, size_t size);
>> #endif
>
> Zbyszek
More information about the systemd-devel
mailing list