]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd-network/sd-dhcp-server.c
networkd: Add EmitRouter= option for DHCP Server (#3251)
[thirdparty/systemd.git] / src / libsystemd-network / sd-dhcp-server.c
index a2a8c16fc8a69dcc6e9a1f1e6a38627ab7c4ced3..fb335337c4515dc7478bf9f59b850f76584606c0 100644 (file)
@@ -1,5 +1,3 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
 /***
   This file is part of systemd.
 
@@ -24,6 +22,7 @@
 
 #include "sd-dhcp-server.h"
 
+#include "alloc-util.h"
 #include "dhcp-internal.h"
 #include "dhcp-server-internal.h"
 #include "fd-util.h"
@@ -184,7 +183,7 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) {
 }
 
 int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
-        _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+        _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL;
 
         assert_return(ret, -EINVAL);
         assert_return(ifindex > 0, -EINVAL);
@@ -209,8 +208,7 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) {
         return 0;
 }
 
-int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event,
-                                int priority) {
+int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) {
         int r;
 
         assert_return(server, -EINVAL);
@@ -282,10 +280,11 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server,
 }
 
 static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination,
+                                uint16_t destination_port,
                                 DHCPMessage *message, size_t len) {
         union sockaddr_union dest = {
                 .in.sin_family = AF_INET,
-                .in.sin_port = htobe16(DHCP_PORT_CLIENT),
+                .in.sin_port = htobe16(destination_port),
                 .in.sin_addr.s_addr = destination,
         };
         struct iovec iov = {
@@ -344,6 +343,7 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
                             DHCPRequest *req, DHCPPacket *packet,
                             int type, size_t optoffset) {
         be32_t destination = INADDR_ANY;
+        uint16_t destination_port = DHCP_PORT_CLIENT;
         int r;
 
         assert(server);
@@ -353,13 +353,13 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
         assert(packet);
 
         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
-                               DHCP_OPTION_SERVER_IDENTIFIER,
+                               SD_DHCP_OPTION_SERVER_IDENTIFIER,
                                4, &server->address);
         if (r < 0)
                 return r;
 
         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &optoffset, 0,
-                               DHCP_OPTION_END, 0, NULL);
+                               SD_DHCP_OPTION_END, 0, NULL);
         if (r < 0)
                 return r;
 
@@ -388,17 +388,19 @@ int dhcp_server_send_packet(sd_dhcp_server *server,
          */
         if (req->message->giaddr) {
                 destination = req->message->giaddr;
+                destination_port = DHCP_PORT_SERVER;
                 if (type == DHCP_NAK)
                         packet->dhcp.flags = htobe16(0x8000);
         } else if (req->message->ciaddr && type != DHCP_NAK)
                 destination = req->message->ciaddr;
 
         if (destination != INADDR_ANY)
-                return dhcp_server_send_udp(server, destination, &packet->dhcp,
+                return dhcp_server_send_udp(server, destination,
+                                            destination_port, &packet->dhcp,
                                             sizeof(DHCPMessage) + optoffset);
         else if (requested_broadcast(req) || type == DHCP_NAK)
                 return dhcp_server_send_udp(server, INADDR_BROADCAST,
-                                            &packet->dhcp,
+                                            destination_port, &packet->dhcp,
                                             sizeof(DHCPMessage) + optoffset);
         else
                 /* we cannot send UDP packet to specific MAC address when the
@@ -456,20 +458,22 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req,
 
         lease_time = htobe32(req->lifetime);
         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
-                               DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
+                               SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
                                &lease_time);
         if (r < 0)
                 return r;
 
         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
-                               DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
+                               SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
         if (r < 0)
                 return r;
 
-        r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
-                               DHCP_OPTION_ROUTER, 4, &server->address);
-        if (r < 0)
-                return r;
+        if (server->emit_router) {
+                r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
+                                       SD_DHCP_OPTION_ROUTER, 4, &server->address);
+                if (r < 0)
+                        return r;
+        }
 
         r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset);
         if (r < 0)
@@ -493,25 +497,27 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
 
         lease_time = htobe32(req->lifetime);
         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
-                               DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
+                               SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME, 4,
                                &lease_time);
         if (r < 0)
                 return r;
 
         r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
-                               DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
+                               SD_DHCP_OPTION_SUBNET_MASK, 4, &server->netmask);
         if (r < 0)
                 return r;
 
-        r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
-                               DHCP_OPTION_ROUTER, 4, &server->address);
-        if (r < 0)
-                return r;
+        if (server->emit_router) {
+                r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0,
+                                       SD_DHCP_OPTION_ROUTER, 4, &server->address);
+                if (r < 0)
+                        return r;
+        }
 
         if (server->n_dns > 0) {
                 r = dhcp_option_append(
                                 &packet->dhcp, req->max_optlen, &offset, 0,
-                                DHCP_OPTION_DOMAIN_NAME_SERVER,
+                                SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
                                 sizeof(struct in_addr) * server->n_dns, server->dns);
                 if (r < 0)
                         return r;
@@ -520,7 +526,7 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
         if (server->n_ntp > 0) {
                 r = dhcp_option_append(
                                 &packet->dhcp, req->max_optlen, &offset, 0,
-                                DHCP_OPTION_NTP_SERVER,
+                                SD_DHCP_OPTION_NTP_SERVER,
                                 sizeof(struct in_addr) * server->n_ntp, server->ntp);
                 if (r < 0)
                         return r;
@@ -529,7 +535,7 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req,
         if (server->timezone) {
                 r = dhcp_option_append(
                                 &packet->dhcp, req->max_optlen, &offset, 0,
-                                DHCP_OPTION_NEW_TZDB_TIMEZONE,
+                                SD_DHCP_OPTION_NEW_TZDB_TIMEZONE,
                                 strlen(server->timezone), server->timezone);
                 if (r < 0)
                         return r;
@@ -575,13 +581,14 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address,
                 return r;
 
         r = dhcp_option_append(&packet->dhcp, DHCP_MIN_OPTIONS_SIZE,
-                               &optoffset, 0, DHCP_OPTION_END, 0, NULL);
+                               &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL);
         if (r < 0)
                 return r;
 
         memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN);
 
-        r = dhcp_server_send_udp(server, address, &packet->dhcp,
+        r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT,
+                                 &packet->dhcp,
                                  sizeof(DHCPMessage) + optoffset);
         if (r < 0)
                 return r;
@@ -595,22 +602,22 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
         assert(req);
 
         switch(code) {
-        case DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
+        case SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME:
                 if (len == 4)
                         req->lifetime = be32toh(*(be32_t*)option);
 
                 break;
-        case DHCP_OPTION_REQUESTED_IP_ADDRESS:
+        case SD_DHCP_OPTION_REQUESTED_IP_ADDRESS:
                 if (len == 4)
                         req->requested_ip = *(be32_t*)option;
 
                 break;
-        case DHCP_OPTION_SERVER_IDENTIFIER:
+        case SD_DHCP_OPTION_SERVER_IDENTIFIER:
                 if (len == 4)
                         req->server_id = *(be32_t*)option;
 
                 break;
-        case DHCP_OPTION_CLIENT_IDENTIFIER:
+        case SD_DHCP_OPTION_CLIENT_IDENTIFIER:
                 if (len >= 2) {
                         uint8_t *data;
 
@@ -624,7 +631,7 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us
                 }
 
                 break;
-        case DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
+        case SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE:
                 if (len == 2)
                         req->max_optlen = be16toh(*(be16_t*)option) -
                                           - sizeof(DHCPPacket);
@@ -698,6 +705,7 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
 int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                                size_t length) {
         _cleanup_dhcp_request_free_ DHCPRequest *req = NULL;
+        _cleanup_free_ char *error_message = NULL;
         DHCPLease *existing_lease;
         int type, r;
 
@@ -713,7 +721,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
         if (!req)
                 return -ENOMEM;
 
-        type = dhcp_option_parse(message, length, parse_request, req);
+        type = dhcp_option_parse(message, length, parse_request, req, &error_message);
         if (type < 0)
                 return 0;
 
@@ -752,7 +760,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
 
                         siphash24_init(&state, HASH_KEY.bytes);
                         client_id_hash_func(&req->client_id, &state);
-                        siphash24_finalize((uint8_t*)&hash, &state);
+                        hash = htole64(siphash24_finalize(&state));
                         next_offer = hash % server->pool_size;
 
                         for (i = 0; i < server->pool_size; i++) {
@@ -783,8 +791,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                 break;
         }
         case DHCP_DECLINE:
-                log_dhcp_server(server, "DECLINE (0x%x)",
-                                be32toh(req->message->xid));
+                log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message));
 
                 /* TODO: make sure we don't offer this address again */
 
@@ -956,16 +963,15 @@ static int server_receive_message(sd_event_source *s, int fd,
                 .msg_controllen = sizeof(cmsgbuf),
         };
         struct cmsghdr *cmsg;
-        int buflen = 0, len;
+        ssize_t buflen, len;
 
         assert(server);
 
-        if (ioctl(fd, FIONREAD, &buflen) < 0)
-                return -errno;
+        buflen = next_datagram_size_fd(fd);
         if (buflen < 0)
-                return -EIO;
+                return buflen;
 
-        message = malloc0(buflen);
+        message = malloc(buflen);
         if (!message)
                 return -ENOMEM;
 
@@ -973,9 +979,12 @@ static int server_receive_message(sd_event_source *s, int fd,
         iov.iov_len = buflen;
 
         len = recvmsg(fd, &msg, 0);
-        if (len < buflen)
-                return 0;
-        else if ((size_t)len < sizeof(DHCPMessage))
+        if (len < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                return -errno;
+        } else if ((size_t)len < sizeof(DHCPMessage))
                 return 0;
 
         CMSG_FOREACH(cmsg, &msg) {
@@ -1153,3 +1162,14 @@ int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], u
 
         return 1;
 }
+
+int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) {
+        assert_return(server, -EINVAL);
+
+        if (enabled == server->emit_router)
+                return 0;
+
+        server->emit_router = enabled;
+
+        return 1;
+}