+ return 1;
+
+ break;
+ case DHCP_REQUEST:
+ {
+ be32_t address;
+ bool init_reboot = false;
+ int pool_offset;
+
+ /* 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 more carefully if IP is correct */
+ address = req->requested_ip;
+ init_reboot = true;
+ } 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;
+ }
+
+ pool_offset = get_pool_offset(server, address);
+
+ /* verify that the requested address is from the pool, and either
+ owned by the current client or free */
+ if (pool_offset >= 0 &&
+ server->bound_leases[pool_offset] == existing_lease) {
+ DHCPLease *lease;
+ usec_t time_now;
+
+ if (!existing_lease) {
+ lease = new0(DHCPLease, 1);
+ lease->address = req->requested_ip;
+ lease->client_id.data = memdup(req->client_id.data,
+ req->client_id.length);
+ if (!lease->client_id.data) {
+ free(lease);
+ return -ENOMEM;
+ }
+ lease->client_id.length = req->client_id.length;
+ memcpy(&lease->chaddr, &req->message->chaddr, ETH_ALEN);
+ lease->gateway = req->message->giaddr;
+ } else
+ lease = existing_lease;
+
+ r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now);
+ if (r < 0)
+ time_now = now(clock_boottime_or_monotonic());
+ lease->expiration = req->lifetime * USEC_PER_SEC + time_now;
+
+ r = server_send_ack(server, req, address);
+ if (r < 0) {
+ /* this only fails on critical errors */
+ log_dhcp_server(server, "could not send ack: %s",
+ strerror(-r));
+
+ if (!existing_lease)
+ dhcp_lease_free(lease);
+
+ return r;
+ } else {
+ log_dhcp_server(server, "ACK (0x%x)",
+ be32toh(req->message->xid));
+
+ server->bound_leases[pool_offset] = lease;
+ hashmap_put(server->leases_by_client_id, &lease->client_id, lease);
+
+ return DHCP_ACK;
+ }
+ } else if (init_reboot) {
+ r = server_send_nak(server, req);
+ if (r < 0) {
+ /* this only fails on critical errors */
+ log_dhcp_server(server, "could not send nak: %s",
+ strerror(-r));
+ return r;
+ } else {
+ log_dhcp_server(server, "NAK (0x%x)",
+ be32toh(req->message->xid));
+ return DHCP_NAK;
+ }
+ }
+
+ break;
+ }
+ case DHCP_RELEASE: {
+ int pool_offset;
+
+ log_dhcp_server(server, "RELEASE (0x%x)",
+ be32toh(req->message->xid));
+
+ if (!existing_lease)
+ return 0;
+
+ if (existing_lease->address != req->message->ciaddr)
+ return 0;
+
+ pool_offset = get_pool_offset(server, req->message->ciaddr);
+ if (pool_offset < 0)
+ return 0;
+
+ if (server->bound_leases[pool_offset] == existing_lease) {
+ server->bound_leases[pool_offset] = NULL;
+ hashmap_remove(server->leases_by_client_id, existing_lease);
+ dhcp_lease_free(existing_lease);
+
+ return 1;
+ } else
+ return 0;
+ }
+ }
+
+ return 0;