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) > 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 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel