]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #1097 from teg/dhcp-server-2
authorDavid Herrmann <dh.herrmann@googlemail.com>
Mon, 31 Aug 2015 21:41:34 +0000 (23:41 +0200)
committerDavid Herrmann <dh.herrmann@googlemail.com>
Mon, 31 Aug 2015 21:41:34 +0000 (23:41 +0200)
dhcp-server: make pool configurable

man/systemd.network.xml
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/test-dhcp-server.c
src/network/networkd-link.c
src/network/networkd-network-gperf.gperf
src/network/networkd-network.h
src/shared/conf-parser.c
src/shared/conf-parser.h
src/systemd/sd-dhcp-server.h

index ded2c0ceff46f9038f6248818fd9c413529ca9a6..2fb4733494384d60a1809e07110e6bc03c288266 100644 (file)
 
     <variablelist class='network-directives'>
 
+      <varlistentry>
+        <term><varname>PoolOffset=</varname></term>
+        <term><varname>PoolSize=</varname></term>
+
+        <listitem><para>Configures the pool of addresses to hand out. The pool
+        is a contiguous sequence of IP addresses in the subnet configured for
+        the server address, which does not include the subnet nor the broadcast
+        address. <varname>PoolOffset=</varname> takes the offset of the pool
+        from the start of subnet, or zero to use the default value.
+        <varname>PoolSize=</varname> takes the number of IP addresses in the
+        pool or zero to use the default value. By default the pool starts at
+        the first address after the subnet address and takes up the rest of
+        the subnet, excluding the broadcast address. If the pool includes
+        the server address (the default), this is reserved and not handed
+        out to clients.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>DefaultLeaseTimeSec=</varname></term>
         <term><varname>MaxLeaseTimeSec=</varname></term>
index 268210fc50000f88cd69157b078a4e2b4ce16f63..5dc3c7aa2624d38ea0b4e5a65e29db224468b256 100644 (file)
@@ -57,8 +57,9 @@ struct sd_dhcp_server {
         int ifindex;
         be32_t address;
         be32_t netmask;
-        be32_t pool_start;
-        size_t pool_size;
+        be32_t subnet;
+        uint32_t pool_offset;
+        uint32_t pool_size;
 
         char *timezone;
 
@@ -67,6 +68,7 @@ struct sd_dhcp_server {
 
         Hashmap *leases_by_client_id;
         DHCPLease **bound_leases;
+        DHCPLease invalid_lease;
 
         uint32_t max_lease_time, default_lease_time;
 };
index 89b9a4c6be9480641789e7949e277788a2955e5b..7a8b298b514793fd68ac98dfab2ff89caef78c4c 100644 (file)
@@ -22,6 +22,7 @@
 
 #include <sys/ioctl.h>
 
+#include "in-addr-util.h"
 #include "siphash24.h"
 
 #include "sd-dhcp-server.h"
 #define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR
 #define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12)
 
-int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server,
-                                  struct in_addr *address,
-                                  size_t size) {
+/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet
+ * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address
+ * moreover, the server's own address may be in the pool, and is in that case reserved in order not to
+ * accidentally hand it out */
+int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) {
+        struct in_addr netmask_addr;
+        be32_t netmask;
+        uint32_t server_off, broadcast_off, size_max;
+
         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);
-        assert_return(!server->bound_leases, -EBUSY);
+        assert_return(address->s_addr != INADDR_ANY, -EINVAL);
+        assert_return(prefixlen <= 32, -ERANGE);
+        assert_return(server->address == INADDR_ANY, -EBUSY);
+
+        assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen));
+        netmask = netmask_addr.s_addr;
+
+        server_off = be32toh(address->s_addr & ~netmask);
+        broadcast_off = be32toh(~netmask);
+
+        /* the server address cannot be the subnet address */
+        assert_return(server_off != 0, -ERANGE);
+
+        /* nor the broadcast address */
+        assert_return(server_off != broadcast_off, -ERANGE);
+
+        /* 0 offset means we should set a default, we skip the first (subnet) address
+           and take the next one */
+        if (offset == 0)
+                offset = 1;
+
+        size_max = (broadcast_off + 1) /* the number of addresses in the subnet */
+                   - offset /* exclude the addresses before the offset */
+                   - 1; /* exclude the last (broadcast) address */
+
+        /* The pool must contain at least one address */
+        assert_return(size_max >= 1, -ERANGE);
+
+        if (size != 0)
+                assert_return(size <= size_max, -ERANGE);
+        else
+                size = size_max;
 
         server->bound_leases = new0(DHCPLease*, size);
         if (!server->bound_leases)
                 return -ENOMEM;
 
-        server->pool_start = address->s_addr;
+        server->pool_offset = offset;
         server->pool_size = size;
 
-        return 0;
-}
-
-int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address,
-                               unsigned char prefixlen) {
-        assert_return(server, -EINVAL);
-        assert_return(address, -EINVAL);
-        assert_return(address->s_addr, -EINVAL);
-        assert_return(prefixlen <= 32, -ERANGE);
-        assert_return(server->address == htobe32(INADDR_ANY), -EBUSY);
-        assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY);
-
         server->address = address->s_addr;
-        server->netmask = htobe32(0xfffffffflu << (32 - prefixlen));
+        server->netmask = netmask;
+        server->subnet = address->s_addr & netmask;
+
+        if (server_off >= offset && server_off - offset < size)
+                server->bound_leases[server_off - offset] = &server->invalid_lease;
 
         return 0;
 }
@@ -661,12 +687,11 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) {
         if (!server->pool_size)
                 return -EINVAL;
 
-        if (be32toh(requested_ip) < be32toh(server->pool_start) ||
-            be32toh(requested_ip) >= be32toh(server->pool_start) +
-                                             + server->pool_size)
-                return -EINVAL;
+        if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) ||
+            be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size)))
+                return -ERANGE;
 
-        return be32toh(requested_ip) - be32toh(server->pool_start);
+        return be32toh(requested_ip & ~server->netmask) - server->pool_offset;
 }
 
 #define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30)
@@ -718,7 +743,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
                 if (existing_lease)
                         address = existing_lease->address;
                 else {
-                        size_t next_offer;
+                        uint32_t next_offer;
 
                         /* even with no persistence of leases, we try to offer the same client
                            the same IP address. we do this by using the hash of the client id
@@ -728,7 +753,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
 
                         for (i = 0; i < server->pool_size; i++) {
                                 if (!server->bound_leases[next_offer]) {
-                                        address = htobe32(be32toh(server->pool_start) + next_offer);
+                                        address = server->subnet | htobe32(server->pool_offset + next_offer);
                                         break;
                                 } else
                                         next_offer = (next_offer + 1) % server->pool_size;
@@ -1022,7 +1047,7 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) {
         for (i = 0; i < server->pool_size; i++) {
                 DHCPLease *lease = server->bound_leases[i];
 
-                if (!lease)
+                if (!lease || lease == &server->invalid_lease)
                         continue;
 
                 r = server_send_forcerenew(server, lease->address,
index 9f60ab761e56f9193f8e980a87aae7103e19c6d0..7d8a1f6bd9bdb27465bceb67ee2992b488f651a3 100644 (file)
 #include "sd-dhcp-server.h"
 #include "dhcp-server-internal.h"
 
+static void test_pool(struct in_addr *address, unsigned size, int ret) {
+        _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
+
+        assert_se(sd_dhcp_server_new(&server, 1) >= 0);
+
+        assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret);
+}
+
 static int test_basic(sd_event *event) {
         _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL;
         struct in_addr address_lo = {
@@ -54,15 +62,14 @@ static int test_basic(sd_event *event) {
         assert_se(!sd_dhcp_server_unref(server));
 
         assert_se(sd_dhcp_server_start(server) == -EUNATCH);
-        assert_se(sd_dhcp_server_set_address(server, &address_any, 28) == -EINVAL);
-        assert_se(sd_dhcp_server_set_address(server, &address_lo, 38) == -ERANGE);
-        assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0);
-        assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) == -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_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL);
+        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE);
+        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
+        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY);
+
+        test_pool(&address_any, 1, -EINVAL);
+        test_pool(&address_lo, 1, 0);
 
         r = sd_dhcp_server_start(server);
 
@@ -119,12 +126,10 @@ static void test_message_handler(void) {
         };
 
         assert_se(sd_dhcp_server_new(&server, 1) >= 0);
-        assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0);
+        assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0);
         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;
index d797a8ded8d174a5a3549217035693c4e582c748..979f3115f6d25073600accc1c173b55d3d0aeedc 100644 (file)
@@ -730,7 +730,6 @@ static int link_enter_set_addresses(Link *link) {
         /* now that we can figure out a default address for the dhcp server,
            start it */
         if (link_dhcp4_server_enabled(link)) {
-                struct in_addr pool_start;
                 Address *address;
                 Link *uplink = NULL;
                 bool acquired_uplink = false;
@@ -742,16 +741,9 @@ static int link_enter_set_addresses(Link *link) {
                         return 0;
                 }
 
-                r = sd_dhcp_server_set_address(link->dhcp_server,
-                                               &address->in_addr.in,
-                                               address->prefixlen);
-                if (r < 0)
-                        return r;
-
-                /* offer 32 addresses starting from the address following the server address */
-                pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1);
-                r = sd_dhcp_server_set_lease_pool(link->dhcp_server,
-                                                  &pool_start, 32);
+                /* use the server address' subnet as the pool */
+                r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
+                                                  link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
                 if (r < 0)
                         return r;
 
@@ -760,11 +752,6 @@ static int link_enter_set_addresses(Link *link) {
                                               &main_address->in_addr.in);
                 if (r < 0)
                         return r;
-
-                r = sd_dhcp_server_set_prefixlen(link->dhcp_server,
-                                                 main_address->prefixlen);
-                if (r < 0)
-                        return r;
                 */
 
                 if (link->network->dhcp_server_max_lease_time_usec > 0) {
index 108e892fb8623b7804e167914dbfce8327acfff8..10ca9dae35794ab270d36ce5faae8860445f2997 100644 (file)
@@ -82,6 +82,8 @@ DHCPServer.EmitNTP,            config_parse_bool,                              0
 DHCPServer.NTP,                config_parse_dhcp_server_ntp,                   0,                             0
 DHCPServer.EmitTimezone,       config_parse_bool,                              0,                             offsetof(Network, dhcp_server_emit_timezone)
 DHCPServer.Timezone,           config_parse_timezone,                          0,                             offsetof(Network, dhcp_server_timezone)
+DHCPServer.PoolOffset,         config_parse_uint32,                            0,                             offsetof(Network, dhcp_server_pool_offset)
+DHCPServer.PoolSize,           config_parse_uint32,                            0,                             offsetof(Network, dhcp_server_pool_size)
 Bridge.Cost,                   config_parse_unsigned,                          0,                             offsetof(Network, cost)
 Bridge.UseBPDU,                config_parse_bool,                              0,                             offsetof(Network, use_bpdu)
 Bridge.HairPin,                config_parse_bool,                              0,                             offsetof(Network, hairpin)
index d691cc3a45a99f7a9ad78075ce201ad77d6a3c38..c3439a70ba2b4e78b8c5ccc767337e39e5150c41 100644 (file)
@@ -102,6 +102,8 @@ struct Network {
         bool dhcp_server_emit_timezone;
         char *dhcp_server_timezone;
         usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec;
+        uint32_t dhcp_server_pool_offset;
+        uint32_t dhcp_server_pool_size;
 
         /* IPV4LL Support */
         AddressFamilyBoolean link_local;
index d99aa1d6e952ebac30a17ef8da9aab7ca0aad848..1f4aea6d6be38ada80a990d3845fb6bc1a639f3b 100644 (file)
@@ -450,6 +450,7 @@ int config_parse_many(const char *conf_file,
 
 DEFINE_PARSER(int, int, safe_atoi)
 DEFINE_PARSER(long, long, safe_atoli)
+DEFINE_PARSER(uint32, uint32_t, safe_atou32)
 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
 DEFINE_PARSER(unsigned, unsigned, safe_atou)
 DEFINE_PARSER(double, double, safe_atod)
index 6152ee33b93af8bb28113c84bef6850c8e32e24b..66c80890d3ea99e1b437425d18e459a70074c6c0 100644 (file)
@@ -104,6 +104,7 @@ int config_parse_many(const char *conf_file,      /* possibly NULL */
 int config_parse_int(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_unsigned(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_long(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_uint32(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_uint64(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_double(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line,  const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_iec_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
index 7e4f2ffb3027189571ad7752ac82593355db7e86..4b0c7a18526804e7c9f622abb7f7699f66ebc0c1 100644 (file)
@@ -44,8 +44,7 @@ bool sd_dhcp_server_is_running(sd_dhcp_server *server);
 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, unsigned char prefixlen);
-int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *start, size_t size);
+int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size);
 
 int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone);
 int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n);