[systemd-commits] 6 commits - man/systemd.network.xml network/80-container-host0.network src/libsystemd-network src/network src/systemd units/local-fs.target

Tom Gundersen tomegun at kemper.freedesktop.org
Sun Jun 29 07:25:18 PDT 2014


 man/systemd.network.xml                      |   15 +-
 network/80-container-host0.network           |    3 
 src/libsystemd-network/dhcp-lease-internal.h |    9 +
 src/libsystemd-network/dhcp-protocol.h       |    1 
 src/libsystemd-network/network-internal.c    |   86 +++++++++++
 src/libsystemd-network/network-internal.h    |    7 
 src/libsystemd-network/sd-dhcp-client.c      |    3 
 src/libsystemd-network/sd-dhcp-lease.c       |  158 +++++++++++++++++++++
 src/libsystemd-network/sd-dhcp-server.c      |    6 
 src/network/networkd-address.c               |    8 +
 src/network/networkd-link.c                  |  197 +++++++++++++++++++++++++--
 src/network/networkd-network-gperf.gperf     |   10 +
 src/network/networkd-network.c               |    1 
 src/network/networkd.h                       |   23 ++-
 src/network/test-network.c                   |   59 ++++++++
 src/systemd/sd-dhcp-client.h                 |    1 
 src/systemd/sd-dhcp-lease.h                  |    4 
 src/systemd/sd-dhcp-server.h                 |    3 
 units/local-fs.target                        |    2 
 19 files changed, 569 insertions(+), 27 deletions(-)

New commits:
commit 1f87d09e984ce34f29a63e4d50ad6ffc3701e078
Author: Tom Gundersen <teg at jklm.no>
Date:   Sun Jun 29 16:20:33 2014 +0200

    units: local-fs.target - don't pull in default dependencies
    
    Reported by Gerardo Exequiel Pozzi:
    
    Looks like [commit a4a878d0] also changes a unrelated file
    (units/local-fs.target) [partially]reverting the commit
    40f862e3 (filesystem targets: disable default dependencies)
    
    The side effect, at least in my case is that the "nofail" option in both
    "crypttab" and "fstab" has partial effect does the default timeout
    instead of continue normal boot without timeout.

diff --git a/units/local-fs.target b/units/local-fs.target
index 70cb13f..d2e5429 100644
--- a/units/local-fs.target
+++ b/units/local-fs.target
@@ -8,6 +8,8 @@
 [Unit]
 Description=Local File Systems
 Documentation=man:systemd.special(7)
+DefaultDependencies=no
+Conflicts=shutdown.target
 After=local-fs-pre.target
 OnFailure=emergency.target
 OnFailureJobMode=replace-irreversibly

commit fb6730c4989e3ad561ec67c70cbd01819824e438
Author: Tom Gundersen <teg at jklm.no>
Date:   Sat Jun 28 16:21:58 2014 +0200

    networkd: set static addresses immediately
    
    Don't wait for IPv4LL nor DHCP to finish before setting statically configured addresses.

diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 82e517e..4c75fa3 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1756,10 +1756,7 @@ static int link_enslaved(Link *link) {
                 }
         }
 
-        if ((link->network->dhcp == DHCP_SUPPORT_NONE) && !link->network->ipv4ll)
-                return link_enter_set_addresses(link);
-
-        return 0;
+        return link_enter_set_addresses(link);
 }
 
 static int enslave_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {

commit e1ea665edac17d75fce01b72dadfa3211b60df2c
Author: Eugene Yakubovich <eyakubovich at gmail.com>
Date:   Fri Jun 27 15:00:06 2014 -0700

    Add support for DHCP static route options
    
    This adds support for DHCP options 33 and 121: Static Route and
    Classless Static Route. To enable this feature, set UseRoutes=true
    in .network file. Returned routes are added to the routing table.

diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 67f10e1..f802129 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -389,6 +389,13 @@
                                         </listitem>
                                 </varlistentry>
                                 <varlistentry>
+                                        <term><varname>UseRoutes=</varname></term>
+                                        <listitem>
+                                                <para>When true (the default), the static routes will be requested from the DHCP server
+                                                and added to the routing table with metric of 1024.</para>
+                                        </listitem>
+                                </varlistentry>
+                                <varlistentry>
                                         <term><varname>CriticalConnection=</varname></term>
                                         <listitem>
                                                 <para>When true, the connection will never be torn down even if the DHCP lease
diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h
index ff09583..d4675f3 100644
--- a/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libsystemd-network/dhcp-lease-internal.h
@@ -32,6 +32,12 @@
 
 #include "sd-dhcp-client.h"
 
+struct sd_dhcp_route {
+        struct in_addr dst_addr;
+        struct in_addr gw_addr;
+        uint8_t dst_prefixlen;
+};
+
 struct sd_dhcp_lease {
         RefCount n_ref;
 
@@ -52,8 +58,9 @@ struct sd_dhcp_lease {
         size_t ntp_size;
         struct in_addr *policy_filter;
         size_t policy_filter_size;
-        struct in_addr *static_route;
+        struct sd_dhcp_route *static_route;
         size_t static_route_size;
+        size_t static_route_allocated;
         uint16_t boot_file_size;
         uint16_t mdr;
         uint16_t mtu;
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index 4d87891..8cbd98e 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -132,5 +132,6 @@ enum {
         DHCP_OPTION_RENEWAL_T1_TIME             = 58,
         DHCP_OPTION_REBINDING_T2_TIME           = 59,
         DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
+        DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121,
         DHCP_OPTION_END                         = 255,
 };
diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
index 97217c1..a5db0c5 100644
--- a/src/libsystemd-network/network-internal.c
+++ b/src/libsystemd-network/network-internal.c
@@ -28,6 +28,7 @@
 #include "siphash24.h"
 #include "libudev-private.h"
 #include "network-internal.h"
+#include "dhcp-lease-internal.h"
 #include "log.h"
 #include "utf8.h"
 #include "util.h"
@@ -432,3 +433,88 @@ int deserialize_in6_addrs(struct in6_addr **ret, size_t *ret_size, const char *s
 
         return 0;
 }
+
+void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size) {
+        unsigned i;
+
+        assert(f);
+        assert(key);
+        assert(routes);
+        assert(size);
+
+        fprintf(f, "%s=", key);
+
+        for (i = 0; i < size; i++)
+                fprintf(f, "%s/%" PRIu8 ",%s%s", inet_ntoa(routes[i].dst_addr),
+                        routes[i].dst_prefixlen, inet_ntoa(routes[i].gw_addr),
+                        (i < (size - 1)) ? " ": "");
+
+        fputs("\n", f);
+}
+
+int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) {
+        _cleanup_free_ struct sd_dhcp_route *routes = NULL;
+        size_t size = 0, allocated = 0;
+        char *word, *state;
+        size_t len;
+
+        assert(ret);
+        assert(ret_size);
+        assert(ret_allocated);
+        assert(string);
+
+        FOREACH_WORD(word, len, string, state) {
+                /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */
+                _cleanup_free_ char* entry;
+                char *tok, *tok_end;
+                unsigned n;
+                int r;
+
+                if (!GREEDY_REALLOC(routes, allocated, size + 1))
+                        return -ENOMEM;
+
+                entry = strndup(word, len);
+
+                tok = entry;
+
+                /* get the subnet */
+                tok_end = strchr(tok, '/');
+                if (!tok_end)
+                        continue;
+                *tok_end = '\0';
+
+                r = inet_aton(tok, &routes[size].dst_addr);
+                if (r == 0)
+                        continue;
+
+                tok = tok_end + 1;
+
+                /* get the prefixlen */
+                tok_end = strchr(tok, ',');
+                if (!tok_end)
+                        continue;
+
+                *tok_end = '\0';
+
+                r = safe_atou(tok, &n);
+                if (r < 0 || n > 32)
+                        continue;
+
+                routes[size].dst_prefixlen = (uint8_t) n;
+                tok = tok_end + 1;
+
+                /* get the gateway */
+                r = inet_aton(tok, &routes[size].gw_addr);
+                if (r == 0)
+                        continue;
+
+                size++;
+        }
+
+        *ret_size = size;
+        *ret_allocated = allocated;
+        *ret = routes;
+        routes = NULL;
+
+        return 0;
+}
diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h
index db48c2c..4bde146 100644
--- a/src/libsystemd-network/network-internal.h
+++ b/src/libsystemd-network/network-internal.h
@@ -70,3 +70,10 @@ const char *net_get_name(struct udev_device *device);
 void serialize_in_addrs(FILE *f, const char *key, struct in_addr *addresses, size_t size);
 int deserialize_in_addrs(struct in_addr **addresses, size_t *size, const char *string);
 int deserialize_in6_addrs(struct in6_addr **addresses, size_t *size, const char *string);
+
+
+/* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
+struct sd_dhcp_route;
+
+void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size);
+int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string);
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 94ba283..20a95f6 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -171,6 +171,22 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) {
         return 0;
 }
 
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes,
+        size_t *routes_size) {
+
+        assert_return(lease, -EINVAL);
+        assert_return(routes, -EINVAL);
+        assert_return(routes_size, -EINVAL);
+
+        if (lease->static_route_size) {
+                *routes = lease->static_route;
+                *routes_size = lease->static_route_size;
+        } else
+                return -ENOENT;
+
+        return 0;
+}
+
 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) {
         if (lease)
                 assert_se(REFCNT_INC(lease->n_ref) >= 2);
@@ -184,6 +200,7 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) {
                 free(lease->domainname);
                 free(lease->dns);
                 free(lease->ntp);
+                free(lease->static_route);
                 free(lease);
         }
 
@@ -301,6 +318,111 @@ static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct
         return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2);
 }
 
+static int class_prefixlen(uint8_t msb_octet, uint8_t *ret) {
+        if (msb_octet < 128)
+                /* Class A */
+                *ret = 8;
+        else if (msb_octet < 192)
+                /* Class B */
+                *ret = 16;
+        else if (msb_octet < 224)
+                /* Class C */
+                *ret = 24;
+        else
+                /* Class D or E -- no subnet mask */
+                return -ERANGE;
+
+        return 0;
+}
+
+static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
+        size_t *routes_size, size_t *routes_allocated) {
+
+        struct in_addr addr;
+
+        assert(option);
+        assert(routes);
+        assert(routes_size);
+        assert(routes_allocated);
+
+        if (!len)
+                return 0;
+
+        if (len % 8 != 0)
+                return -EINVAL;
+
+        if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + (len / 8)))
+                return -ENOMEM;
+
+        while (len >= 8) {
+                struct sd_dhcp_route *route = *routes + *routes_size;
+
+                if (class_prefixlen(*option, &route->dst_prefixlen) < 0) {
+                        log_error("Failed to determine destination prefix length from class based IP, ignoring");
+                        continue;
+                }
+
+                lease_parse_be32(option, 4, &addr.s_addr);
+                route->dst_addr = inet_makeaddr(inet_netof(addr), 0);
+                option += 4;
+
+                lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+                option += 4;
+
+                len -= 8;
+                (*routes_size)++;
+        }
+
+        return 0;
+}
+
+/* parses RFC3442 Classless Static Route Option */
+static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes,
+        size_t *routes_size, size_t *routes_allocated) {
+
+        assert(option);
+        assert(routes);
+        assert(routes_size);
+        assert(routes_allocated);
+
+        /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)*  */
+
+        while (len > 0) {
+                uint8_t dst_octets;
+                struct sd_dhcp_route *route;
+
+                if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1))
+                    return -ENOMEM;
+
+                route = *routes + *routes_size;
+
+                dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1);
+                route->dst_prefixlen = *option;
+                option++;
+                len--;
+
+                /* can't have more than 4 octets in IPv4 */
+                if (dst_octets > 4 || len < dst_octets)
+                        return -EINVAL;
+
+                route->dst_addr.s_addr = 0;
+                memcpy(&route->dst_addr.s_addr, option, dst_octets);
+                option += dst_octets;
+                len -= dst_octets;
+
+                if (len < 4)
+                        return -EINVAL;
+
+                lease_parse_be32(option, 4, &route->gw_addr.s_addr);
+                option += 4;
+                len -= 4;
+
+                (*routes_size)++;
+        }
+
+        return 0;
+}
+
 int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
                               void *user_data) {
         sd_dhcp_lease *lease = user_data;
@@ -367,7 +489,8 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
                 break;
 
         case DHCP_OPTION_STATIC_ROUTE:
-                r = lease_parse_in_addrs_pairs(option, len, &lease->static_route, &lease->static_route_size);
+                r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size,
+                        &lease->static_route_allocated);
                 if (r < 0)
                         return r;
 
@@ -433,6 +556,14 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option,
                 lease_parse_bool(option, len, &lease->ip_forward_non_local);
 
                 break;
+
+        case DHCP_OPTION_CLASSLESS_STATIC_ROUTE:
+                r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size,
+                        &lease->static_route_allocated);
+                if (r < 0)
+                        return r;
+
+                break;
         }
 
         return 0;
@@ -460,6 +591,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         size_t addresses_size;
         const char *string;
         uint16_t mtu;
+        struct sd_dhcp_route *routes;
+        size_t routes_size;
         int r;
 
         assert(lease);
@@ -522,6 +655,10 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
         if (r >= 0)
                 fprintf(f, "ROOT_PATH=%s\n", string);
 
+        r = sd_dhcp_lease_get_routes(lease, &routes, &routes_size);
+        if (r >= 0)
+                serialize_dhcp_routes(f, "ROUTES", routes, routes_size);
+
         r = 0;
 
         fflush(f);
@@ -543,7 +680,7 @@ int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
         _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL;
         _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL,
                             *server_address = NULL, *next_server = NULL,
-                            *dns = NULL, *ntp = NULL, *mtu = NULL;
+                            *dns = NULL, *ntp = NULL, *mtu = NULL, *routes = NULL;
         struct in_addr addr;
         int r;
 
@@ -566,6 +703,7 @@ int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
                            "DOMAINNAME", &lease->domainname,
                            "HOSTNAME", &lease->hostname,
                            "ROOT_PATH", &lease->root_path,
+                           "ROUTES", &routes,
                            NULL);
         if (r < 0) {
                 if (r == -ENOENT)
@@ -629,6 +767,13 @@ int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) {
                         lease->mtu = u;
         }
 
+        if (routes) {
+                r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size,
+                                &lease->static_route_allocated, routes);
+                if (r < 0)
+                    return r;
+        }
+
         *ret = lease;
         lease = NULL;
 
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 5d280b5..82e517e 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -402,6 +402,51 @@ static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
         return 1;
 }
 
+static int link_set_dhcp_routes(Link *link) {
+        struct sd_dhcp_route *static_routes;
+        size_t static_routes_size;
+        int r;
+        unsigned i;
+
+        assert(link);
+
+        r = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes, &static_routes_size);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        log_warning_link(link, "DHCP error: %s", strerror(-r));
+                return r;
+        }
+
+        for (i = 0; i < static_routes_size; i++) {
+                _cleanup_route_free_ Route *route = NULL;
+
+                r = route_new_dynamic(&route);
+                if (r < 0) {
+                        log_error_link(link, "Could not allocate route: %s",
+                                       strerror(-r));
+                        return r;
+                }
+
+                route->family = AF_INET;
+                route->in_addr.in = static_routes[i].gw_addr;
+                route->dst_addr.in = static_routes[i].dst_addr;
+                route->dst_prefixlen = static_routes[i].dst_prefixlen;
+                route->metrics = DHCP_STATIC_ROUTE_METRIC;
+
+                r = route_configure(route, link, &route_handler);
+                if (r < 0) {
+                        log_warning_link(link,
+                                         "could not set host route: %s", strerror(-r));
+                        return r;
+                }
+
+                link_ref(link);
+                link->route_messages ++;
+        }
+
+        return 0;
+}
+
 static int link_enter_set_routes(Link *link) {
         Route *rt;
         int r;
@@ -525,6 +570,9 @@ static int link_enter_set_routes(Link *link) {
                         link_ref(link);
                         link->route_messages ++;
                 }
+
+                if (link->network->dhcp_routes)
+                        link_set_dhcp_routes(link);
         }
 
         if (link->route_messages == 0) {
@@ -901,12 +949,11 @@ static int link_set_mtu(Link *link, uint32_t mtu) {
 
 static int dhcp_lease_lost(Link *link) {
         _cleanup_address_free_ Address *address = NULL;
-        _cleanup_route_free_ Route *route_gw = NULL;
-        _cleanup_route_free_ Route *route = NULL;
         struct in_addr addr;
         struct in_addr netmask;
         struct in_addr gateway;
         unsigned prefixlen;
+        unsigned i;
         int r;
 
         assert(link);
@@ -914,10 +961,36 @@ static int dhcp_lease_lost(Link *link) {
 
         log_warning_link(link, "DHCP lease lost");
 
+        if (link->network->dhcp_routes) {
+                struct sd_dhcp_route *routes;
+                size_t routes_size;
+
+                r = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes, &routes_size);
+                if (r >= 0) {
+                        for (i = 0; i < routes_size; i++) {
+                                _cleanup_route_free_ Route *route = NULL;
+
+                                r = route_new_dynamic(&route);
+                                if (r >= 0) {
+                                        route->family = AF_INET;
+                                        route->in_addr.in = routes[i].gw_addr;
+                                        route->dst_addr.in = routes[i].dst_addr;
+                                        route->dst_prefixlen = routes[i].dst_prefixlen;
+
+                                        route_drop(route, link, &route_drop_handler);
+                                        link_ref(link);
+                                }
+                        }
+                }
+        }
+
         r = address_new_dynamic(&address);
         if (r >= 0) {
                 r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway);
                 if (r >= 0) {
+                        _cleanup_route_free_ Route *route_gw = NULL;
+                        _cleanup_route_free_ Route *route = NULL;
+
                         r = route_new_dynamic(&route_gw);
                         if (r >= 0) {
                                 route_gw->family = AF_INET;
@@ -1952,6 +2025,14 @@ static int link_configure(Link *link) {
                         if (r < 0)
                                 return r;
                 }
+                if (link->network->dhcp_routes) {
+                        r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_STATIC_ROUTE);
+                        if (r < 0)
+                                return r;
+                        r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
+                        if (r < 0)
+                                return r;
+                }
         }
 
         if (link->network->dhcp_server) {
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index c8610d1..b6d52e7 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -53,4 +53,5 @@ DHCPv4.UseDNS,               config_parse_bool,                  0,
 DHCPv4.UseMTU,               config_parse_bool,                  0,                             offsetof(Network, dhcp_mtu)
 DHCPv4.UseHostname,          config_parse_bool,                  0,                             offsetof(Network, dhcp_hostname)
 DHCPv4.UseDomainName,        config_parse_bool,                  0,                             offsetof(Network, dhcp_domainname)
+DHCPv4.UseRoutes,            config_parse_bool,                  0,                             offsetof(Network, dhcp_routes)
 DHCPv4.CriticalConnection,   config_parse_bool,                  0,                             offsetof(Network, dhcp_critical)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 8e7cdf6..f60f7c8 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -89,6 +89,7 @@ static int network_load_one(Manager *manager, const char *filename) {
         network->dhcp_dns = true;
         network->dhcp_hostname = true;
         network->dhcp_domainname = true;
+        network->dhcp_routes = true;
 
         r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
                         (void*) network_network_gperf_lookup, false, false, network);
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 67f70d9..11268de 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -42,6 +42,7 @@
 
 #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU
 #define VXLAN_VID_MAX (1u << 24) - 1
+#define DHCP_STATIC_ROUTE_METRIC 1024
 
 typedef struct NetDev NetDev;
 typedef struct Network Network;
@@ -169,6 +170,7 @@ struct Network {
         bool dhcp_hostname;
         bool dhcp_domainname;
         bool dhcp_critical;
+        bool dhcp_routes;
         bool ipv4ll;
 
         bool dhcp_server;
diff --git a/src/network/test-network.c b/src/network/test-network.c
index eb6ebe7..75e70fa 100644
--- a/src/network/test-network.c
+++ b/src/network/test-network.c
@@ -21,6 +21,7 @@
 
 #include "networkd.h"
 #include "network-internal.h"
+#include "dhcp-lease-internal.h"
 
 static void test_deserialize_in_addr(void) {
         _cleanup_free_ struct in_addr *addresses = NULL;
@@ -53,6 +54,63 @@ static void test_deserialize_in_addr(void) {
         assert_se(!memcmp(&f, &addresses6[2], sizeof(struct in6_addr)));
 }
 
+static void test_deserialize_dhcp_routes(void) {
+        size_t size, allocated;
+
+        {
+                _cleanup_free_ struct sd_dhcp_route *routes = NULL;
+                assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, "") >= 0);
+                assert_se(size == 0);
+        }
+
+        {
+                /* no errors */
+                _cleanup_free_ struct sd_dhcp_route *routes = NULL;
+                const char *routes_string = "192.168.0.0/16,192.168.0.1 10.1.2.0/24,10.1.2.1 0.0.0.0/0,10.0.1.1";
+
+                assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0);
+
+                assert_se(size == 3);
+                assert_se(routes[0].dst_addr.s_addr == inet_addr("192.168.0.0"));
+                assert_se(routes[0].gw_addr.s_addr == inet_addr("192.168.0.1"));
+                assert_se(routes[0].dst_prefixlen == 16);
+
+                assert_se(routes[1].dst_addr.s_addr == inet_addr("10.1.2.0"));
+                assert_se(routes[1].gw_addr.s_addr == inet_addr("10.1.2.1"));
+                assert_se(routes[1].dst_prefixlen == 24);
+
+                assert_se(routes[2].dst_addr.s_addr == inet_addr("0.0.0.0"));
+                assert_se(routes[2].gw_addr.s_addr == inet_addr("10.0.1.1"));
+                assert_se(routes[2].dst_prefixlen == 0);
+        }
+
+        {
+                /* error in second word */
+                _cleanup_free_ struct sd_dhcp_route *routes = NULL;
+                const char *routes_string = "192.168.0.0/16,192.168.0.1 10.1.2.0#24,10.1.2.1 0.0.0.0/0,10.0.1.1";
+
+                assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0);
+
+                assert_se(size == 2);
+                assert_se(routes[0].dst_addr.s_addr == inet_addr("192.168.0.0"));
+                assert_se(routes[0].gw_addr.s_addr == inet_addr("192.168.0.1"));
+                assert_se(routes[0].dst_prefixlen == 16);
+
+                assert_se(routes[2].dst_addr.s_addr == inet_addr("0.0.0.0"));
+                assert_se(routes[2].gw_addr.s_addr == inet_addr("10.0.1.1"));
+                assert_se(routes[2].dst_prefixlen == 0);
+        }
+
+        {
+                /* error in every word */
+                _cleanup_free_ struct sd_dhcp_route *routes = NULL;
+                const char *routes_string = "192.168.0.0/55,192.168.0.1 10.1.2.0#24,10.1.2.1 0.0.0.0/0,10.0.1.X";
+
+                assert_se(deserialize_dhcp_routes(&routes, &size, &allocated, routes_string) >= 0);
+                assert_se(size == 0);
+        }
+}
+
 static void test_load_config(Manager *manager) {
 /*  TODO: should_reload, is false if the config dirs do not exist, so
  *        so we can't do this test here, move it to a test for paths_check_timestamps
@@ -125,6 +183,7 @@ int main(void) {
         struct udev_device *loopback;
 
         test_deserialize_in_addr();
+        test_deserialize_dhcp_routes();
         test_address_equality();
 
         assert_se(manager_new(&manager) >= 0);
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
index 4e3a054..c15744d 100644
--- a/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/sd-dhcp-lease.h
@@ -27,6 +27,7 @@
 #include <net/ethernet.h>
 
 typedef struct sd_dhcp_lease sd_dhcp_lease;
+struct sd_dhcp_route;
 
 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
@@ -42,5 +43,5 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
 int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname);
 int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path);
-
+int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes, size_t *routes_size);
 #endif

commit ed942a9eb22d50f667909ad6184b45015d28d054
Author: Tom Gundersen <teg at jklm.no>
Date:   Sat Jun 28 16:00:49 2014 +0200

    networkd: merge DHCPv4 and DHCPv6 config
    
    If there are v4 or v6 specific options we can keep those in separate sections,
    but for the common options, we will use only one.
    
    Moreovere only use DHCP=[yes/both|no/none|v4|v6] to enable or disable the clients.

diff --git a/man/systemd.network.xml b/man/systemd.network.xml
index 5bff4d9..67f10e1 100644
--- a/man/systemd.network.xml
+++ b/man/systemd.network.xml
@@ -195,7 +195,9 @@
                                 <varlistentry>
                                         <term><varname>DHCP=</varname></term>
                                         <listitem>
-                                                <para>A boolean. When true, enables basic DHCPv4 support.</para>
+                                                <para>Enables DHCPv4 and/or DHCPv6 support. Accepts
+                                                <literal>both</literal>, <literal>none</literal>,
+                                                <literal>v4</literal> or <literal>v6</literal>.</para>
                                         </listitem>
                                 </varlistentry>
                                 <varlistentry>
@@ -361,8 +363,8 @@
         </refsect1>
 
         <refsect1>
-                <title>[DHCPv4] Section Options</title>
-                        <para>The <literal>[DHCPv4]</literal> section accepts the following keys:</para>
+                <title>[DHCP] Section Options</title>
+                        <para>The <literal>[DHCP]</literal> section accepts the following keys:</para>
 
                         <variablelist class='network-directives'>
                                 <varlistentry>
diff --git a/network/80-container-host0.network b/network/80-container-host0.network
index 76c0f1d..6863ca9 100644
--- a/network/80-container-host0.network
+++ b/network/80-container-host0.network
@@ -10,6 +10,5 @@ Virtualization=container
 Name=host0
 
 [Network]
-DHCP=yes
-DHCPv6=yes
+DHCP=both
 IPv4LL=yes
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 6bff73d..5d280b5 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -29,6 +29,7 @@
 #include "virt.h"
 #include "bus-util.h"
 #include "network-internal.h"
+#include "conf-parser.h"
 
 #include "network-util.h"
 #include "dhcp-lease-internal.h"
@@ -205,7 +206,7 @@ static int link_stop_clients(Link *link) {
         if (!link->network)
                 return 0;
 
-        if (link->network->dhcp) {
+        if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V6)) {
                 assert(link->dhcp_client);
 
                 k = sd_dhcp_client_stop(link->dhcp_client);
@@ -235,7 +236,7 @@ static int link_stop_clients(Link *link) {
                 }
         }
 
-        if (link->network->dhcp6) {
+        if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V6)) {
                 assert(link->icmp6_router_discovery);
 
                 if (link->dhcp6_client) {
@@ -1456,7 +1457,7 @@ static int link_acquire_conf(Link *link) {
                 }
         }
 
-        if (link->network->dhcp) {
+        if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4)) {
                 assert(link->dhcp_client);
 
                 log_debug_link(link, "acquiring DHCPv4 lease");
@@ -1469,7 +1470,7 @@ static int link_acquire_conf(Link *link) {
                 }
         }
 
-        if (link->network->dhcp6) {
+        if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V6)) {
                 assert(link->icmp6_router_discovery);
 
                 log_debug_link(link, "discovering IPv6 routers");
@@ -1682,7 +1683,7 @@ static int link_enslaved(Link *link) {
                 }
         }
 
-        if (!link->network->dhcp && !link->network->ipv4ll)
+        if ((link->network->dhcp == DHCP_SUPPORT_NONE) && !link->network->ipv4ll)
                 return link_enter_set_addresses(link);
 
         return 0;
@@ -1925,7 +1926,7 @@ static int link_configure(Link *link) {
                         return r;
         }
 
-        if (link->network->dhcp) {
+        if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V4)) {
                 r = sd_dhcp_client_new(&link->dhcp_client);
                 if (r < 0)
                         return r;
@@ -1963,7 +1964,7 @@ static int link_configure(Link *link) {
                         return r;
         }
 
-        if (link->network->dhcp6) {
+        if (IN_SET(link->network->dhcp, DHCP_SUPPORT_BOTH, DHCP_SUPPORT_V6)) {
                 r = sd_icmp6_nd_new(&link->icmp6_router_discovery);
                 if (r < 0)
                         return r;
@@ -2496,3 +2497,55 @@ static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState);
+
+static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
+        [DHCP_SUPPORT_NONE] = "none",
+        [DHCP_SUPPORT_BOTH] = "both",
+        [DHCP_SUPPORT_V4] = "v4",
+        [DHCP_SUPPORT_V6] = "v6",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
+
+int config_parse_dhcp(
+                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) {
+
+        DHCPSupport *dhcp = data;
+        int k;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        /* Our enum shall be a superset of booleans, hence first try
+         * to parse as boolean, and then as enum */
+
+        k = parse_boolean(rvalue);
+        if (k > 0)
+                *dhcp = DHCP_SUPPORT_BOTH;
+        else if (k == 0)
+                *dhcp = DHCP_SUPPORT_NONE;
+        else {
+                DHCPSupport s;
+
+                s = dhcp_support_from_string(rvalue);
+                if (s < 0){
+                        log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
+                        return 0;
+                }
+
+                *dhcp = s;
+        }
+
+        return 0;
+}
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 469e028..c8610d1 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -30,10 +30,9 @@ Network.Bond,                config_parse_netdev,                0,
 Network.VLAN,                config_parse_netdev,                0,                             offsetof(Network, vlans)
 Network.MACVLAN,             config_parse_netdev,                0,                             offsetof(Network, macvlans)
 Network.VXLAN,               config_parse_netdev,                0,                             offsetof(Network, vxlans)
-Network.DHCP,                config_parse_bool,                  0,                             offsetof(Network, dhcp)
+Network.DHCP,                config_parse_dhcp,                  0,                             offsetof(Network, dhcp)
 Network.DHCPServer,          config_parse_bool,                  0,                             offsetof(Network, dhcp_server)
 Network.IPv4LL,              config_parse_bool,                  0,                             offsetof(Network, ipv4ll)
-Network.DHCPv6,              config_parse_bool,                  0,                             offsetof(Network, dhcp6)
 Network.Address,             config_parse_address,               0,                             0
 Network.Gateway,             config_parse_gateway,               0,                             0
 Network.DNS,                 config_parse_dns,                   0,                             offsetof(Network, dns)
@@ -44,6 +43,12 @@ Address.Broadcast,           config_parse_broadcast,             0,
 Address.Label,               config_parse_label,                 0,                             0
 Route.Gateway,               config_parse_gateway,               0,                             0
 Route.Destination,           config_parse_destination,           0,                             0
+DHCP.UseDNS,                 config_parse_bool,                  0,                             offsetof(Network, dhcp_dns)
+DHCP.UseMTU,                 config_parse_bool,                  0,                             offsetof(Network, dhcp_mtu)
+DHCP.UseHostname,            config_parse_bool,                  0,                             offsetof(Network, dhcp_hostname)
+DHCP.UseDomainName,          config_parse_bool,                  0,                             offsetof(Network, dhcp_domainname)
+DHCP.CriticalConnection,     config_parse_bool,                  0,                             offsetof(Network, dhcp_critical)
+/* backwards compatibility */
 DHCPv4.UseDNS,               config_parse_bool,                  0,                             offsetof(Network, dhcp_dns)
 DHCPv4.UseMTU,               config_parse_bool,                  0,                             offsetof(Network, dhcp_mtu)
 DHCPv4.UseHostname,          config_parse_bool,                  0,                             offsetof(Network, dhcp_hostname)
diff --git a/src/network/networkd.h b/src/network/networkd.h
index b7b1d90..67f70d9 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -131,6 +131,15 @@ struct NetDev {
         LIST_HEAD(netdev_enslave_callback, callbacks);
 };
 
+typedef enum DHCPSupport {
+        DHCP_SUPPORT_NONE,
+        DHCP_SUPPORT_BOTH,
+        DHCP_SUPPORT_V4,
+        DHCP_SUPPORT_V6,
+        _DHCP_SUPPORT_MAX,
+        _DHCP_SUPPORT_INVALID = -1,
+} DHCPSupport;
+
 struct Network {
         Manager *manager;
 
@@ -153,7 +162,7 @@ struct Network {
         Hashmap *vlans;
         Hashmap *macvlans;
         Hashmap *vxlans;
-        bool dhcp;
+        DHCPSupport dhcp;
         bool dhcp_dns;
         bool dhcp_ntp;
         bool dhcp_mtu;
@@ -161,7 +170,6 @@ struct Network {
         bool dhcp_domainname;
         bool dhcp_critical;
         bool ipv4ll;
-        bool dhcp6;
 
         bool dhcp_server;
 
@@ -472,6 +480,15 @@ LinkOperationalState link_operstate_from_string(const char *s) _pure_;
 DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref);
 #define _cleanup_link_unref_ _cleanup_(link_unrefp)
 
+/* DHCP support */
+
+const char* dhcp_support_to_string(DHCPSupport i) _const_;
+DHCPSupport dhcp_support_from_string(const char *s) _pure_;
+
+int config_parse_dhcp(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);
+
 /* Address Pool */
 
 int address_pool_new(Manager *m, AddressPool **ret, unsigned family, const union in_addr_union *u, unsigned prefixlen);

commit 68ceb9df6a39a7f86ffc3cf8266ca677a5d5271b
Author: Patrik Flykt <Patrik.Flykt at linux.intel.com>
Date:   Thu Jun 26 16:18:43 2014 +0300

    sd-dhcp-client/networkd: set lifetimes for IPv4 addresses
    
    Note that /proc/sys/net/ipv4/ip_dynaddr needs to be non-zero.
    
    [tomegun: hook up DHCP renew events to increase the lifetime when necessary]

diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 1603c41..790728b 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -879,7 +879,8 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack,
                     client->lease->subnet_mask != lease->subnet_mask ||
                     client->lease->router != lease->router) {
                         r = DHCP_EVENT_IP_CHANGE;
-                }
+                } else
+                        r = DHCP_EVENT_RENEW;
 
                 client->lease = sd_dhcp_lease_unref(client->lease);
         }
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 3203b7a..94ba283 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -47,6 +47,15 @@ int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) {
         return 0;
 }
 
+int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) {
+        assert_return(lease, -EINVAL);
+        assert_return(lease, -EINVAL);
+
+        *lifetime = lease->lifetime;
+
+        return 0;
+}
+
 int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
         assert_return(lease, -EINVAL);
         assert_return(mtu, -EINVAL);
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index ce01500..9c3e0e3 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -360,6 +360,14 @@ int address_configure(Address *address, Link *link,
                 }
         }
 
+        r = sd_rtnl_message_append_cache_info(req, IFA_CACHEINFO,
+                                              &address->cinfo);
+        if (r < 0) {
+                log_error("Could not append IFA_CACHEINFO attribute: %s",
+                          strerror(-r));
+                return r;
+        }
+
         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
         if (r < 0) {
                 log_error("Could not send rtnetlink message: %s", strerror(-r));
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 5527489..6bff73d 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -600,6 +600,7 @@ static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
 static int link_enter_set_addresses(Link *link) {
         Address *ad;
         int r;
+        uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME;
 
         assert(link);
         assert(link->network);
@@ -676,6 +677,16 @@ static int link_enter_set_addresses(Link *link) {
                         return r;
                 }
 
+                if (!link->network->dhcp_critical) {
+                        r = sd_dhcp_lease_get_lifetime(link->dhcp_lease,
+                                                       &lifetime);
+                        if (r < 0) {
+                                log_warning_link(link, "DHCP error: no lifetime: %s",
+                                                 strerror(-r));
+                                return r;
+                        }
+                }
+
                 r = sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask);
                 if (r < 0) {
                         log_warning_link(link, "DHCP error: no netmask: %s",
@@ -694,6 +705,8 @@ static int link_enter_set_addresses(Link *link) {
 
                 address->family = AF_INET;
                 address->in_addr.in = addr;
+                address->cinfo.ifa_prefered = lifetime;
+                address->cinfo.ifa_valid = lifetime;
                 address->prefixlen = prefixlen;
                 address->broadcast.s_addr = addr.s_addr | ~netmask.s_addr;
 
@@ -967,6 +980,25 @@ static int dhcp_lease_lost(Link *link) {
         return 0;
 }
 
+static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) {
+        sd_dhcp_lease *lease;
+        int r;
+
+        r = sd_dhcp_client_get_lease(client, &lease);
+        if (r < 0) {
+                log_warning_link(link, "DHCP error: no lease %s",
+                                 strerror(-r));
+                return r;
+        }
+
+        sd_dhcp_lease_unref(link->dhcp_lease);
+        link->dhcp_lease = lease;
+
+        link_enter_set_addresses(link);
+
+        return 0;
+}
+
 static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
         sd_dhcp_lease *lease;
         struct in_addr address;
@@ -1118,6 +1150,13 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
                         }
 
                         break;
+                case DHCP_EVENT_RENEW:
+                        r = dhcp_lease_renew(client, link);
+                        if (r < 0) {
+                                link_enter_failed(link);
+                                return;
+                        }
+                        break;
                 case DHCP_EVENT_IP_ACQUIRE:
                         r = dhcp_lease_acquired(client, link);
                         if (r < 0) {
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 5818ec4..0ed7d0f 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -34,6 +34,7 @@ enum {
         DHCP_EVENT_IP_ACQUIRE                   = 2,
         DHCP_EVENT_IP_CHANGE                    = 3,
         DHCP_EVENT_EXPIRED                      = 4,
+        DHCP_EVENT_RENEW                        = 5,
 };
 
 typedef struct sd_dhcp_client sd_dhcp_client;
diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h
index 252c093..4e3a054 100644
--- a/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/sd-dhcp-lease.h
@@ -31,6 +31,7 @@ typedef struct sd_dhcp_lease sd_dhcp_lease;
 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
 int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
 int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);

commit 7c16313f11e3953f3fe4dbf544f2d36f58d14138
Author: Tom Gundersen <teg at jklm.no>
Date:   Sun Jun 29 13:11:44 2014 +0200

    networkd/sd-dhcp-server: only start dhcp server when necessary

diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c
index c932377..5c32fdf 100644
--- a/src/libsystemd-network/sd-dhcp-server.c
+++ b/src/libsystemd-network/sd-dhcp-server.c
@@ -62,6 +62,12 @@ int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address)
         return 0;
 }
 
+bool sd_dhcp_server_is_running(sd_dhcp_server *server) {
+        assert_return(server, -EINVAL);
+
+        return !!server->receive_message;
+}
+
 sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) {
         if (server)
                 assert_se(REFCNT_INC(server->n_ref) >= 2);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 9296a59..5527489 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -307,7 +307,8 @@ static int link_enter_configured(Link *link) {
         assert(link->network);
         assert(link->state == LINK_STATE_SETTING_ROUTES);
 
-        if (link->network->dhcp_server) {
+        if (link->network->dhcp_server &&
+            !sd_dhcp_server_is_running(link->dhcp_server)) {
                 struct in_addr pool_start;
                 Address *address;
 
diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h
index cd0ff72..01595d3 100644
--- a/src/systemd/sd-dhcp-server.h
+++ b/src/systemd/sd-dhcp-server.h
@@ -23,6 +23,7 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <stdbool.h>
 #include <netinet/in.h>
 
 #include "sd-event.h"
@@ -38,6 +39,8 @@ int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int pri
 int sd_dhcp_server_detach_event(sd_dhcp_server *client);
 sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client);
 
+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);
 



More information about the systemd-commits mailing list