[systemd-devel] [PATCH 11/17] sd-dhcp-server: add basic REQUEST/ACK support

Zbigniew Jędrzejewski-Szmek zbyszek at in.waw.pl
Tue May 27 08:48:18 PDT 2014


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


More information about the systemd-devel mailing list