[systemd-commits] 6 commits - man/systemd-networkd.service.xml src/libsystemd src/libsystemd-dhcp src/network src/shared src/systemd

Tom Gundersen tomegun at kemper.freedesktop.org
Thu Jan 30 05:38:09 PST 2014


 man/systemd-networkd.service.xml       |    8 ++
 src/libsystemd-dhcp/sd-dhcp-client.c   |   15 -----
 src/libsystemd/sd-rtnl/rtnl-internal.h |   27 +++++----
 src/libsystemd/sd-rtnl/rtnl-message.c  |   99 +++++----------------------------
 src/libsystemd/sd-rtnl/rtnl-util.c     |   72 ++++++++++++++++++++++++
 src/libsystemd/sd-rtnl/rtnl-util.h     |   10 +++
 src/libsystemd/sd-rtnl/sd-rtnl.c       |   14 ++--
 src/libsystemd/sd-rtnl/test-rtnl.c     |    5 -
 src/network/networkd-address.c         |   52 +++++++++++++++--
 src/network/networkd-gperf.gperf       |   52 +++++++++--------
 src/network/networkd-link.c            |   26 +++-----
 src/network/networkd-manager.c         |   19 ++++++
 src/network/networkd-netdev.c          |   35 ++++++-----
 src/network/networkd-network.c         |    6 --
 src/network/networkd-route.c           |    8 ++
 src/network/networkd.h                 |   12 ++--
 src/shared/net-util.c                  |   35 +++++++++--
 src/shared/net-util.h                  |    3 +
 src/systemd/sd-dhcp-client.h           |    1 
 19 files changed, 300 insertions(+), 199 deletions(-)

New commits:
commit 50add2909c2e4b13a04d285b058b1c2270137656
Author: Tom Gundersen <teg at jklm.no>
Date:   Wed Jan 29 21:24:44 2014 +0100

    networkd: netdev - reduce chance of race when receiving netdev's ifindex
    
    When creating a new link, the kernel will not inform us about the new ifindex
    in its ack. We have to listen for newly created devices and deduce the new
    ifindex by matching on the ifname.
    
    We used to do this by waiting for a new device from libudev, but that is asking
    for trouble, as udev will happily rename the device before handing it to us.
    Listen on rtnl instead, the chance of the name being changed before reaching us
    is much smaller (if not nil).
    
    Kernel patch in the works to make this unneccessary.

diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index f021918..363602e 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -96,7 +96,6 @@ int link_add(Manager *m, struct udev_device *device, Link **ret) {
         Network *network;
         int r;
         uint64_t ifindex;
-        NetdevKind kind;
 
         assert(m);
         assert(device);
@@ -114,13 +113,6 @@ int link_add(Manager *m, struct udev_device *device, Link **ret) {
 
         *ret = link;
 
-        kind = netdev_kind_from_string(udev_device_get_devtype(device));
-        if (kind != _NETDEV_KIND_INVALID) {
-                r = netdev_set_link(m, kind, link);
-                if (r < 0 && r != -ENOENT)
-                        return r;
-        }
-
         r = network_get(m, device, &network);
         if (r < 0)
                 return r == -ENOENT ? 0 : r;
diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c
index 7b93c5b..a007b04 100644
--- a/src/network/networkd-manager.c
+++ b/src/network/networkd-manager.c
@@ -25,6 +25,7 @@
 #include "networkd.h"
 #include "libudev-private.h"
 #include "udev-util.h"
+#include "rtnl-util.h"
 #include "mkdir.h"
 
 const char* const network_dirs[] = {
@@ -244,15 +245,31 @@ int manager_udev_listen(Manager *m) {
 static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
         Manager *m = userdata;
         Link *link;
+        const char *name;
         uint64_t ifindex_64;
         int r, ifindex;
 
         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
-        if (r < 0) {
+        if (r < 0 || ifindex <= 0) {
                 log_debug("received RTM_NEWLINK message without valid ifindex");
                 return 0;
         }
 
+        r = rtnl_message_link_get_ifname(message, &name);
+        if (r < 0)
+                log_debug("received RTM_NEWLINK message without valid IFLA_IFNAME");
+        else {
+                Netdev *netdev;
+
+                r = netdev_get(m, name, &netdev);
+                if (r >= 0) {
+                        r = netdev_set_ifindex(netdev, ifindex);
+                        if (r < 0)
+                                log_debug("could not set ifindex of netdev '%s' to %d: %s",
+                                          name, ifindex, strerror(-r));
+                }
+        }
+
         ifindex_64 = ifindex;
         link = hashmap_get(m->links, &ifindex_64);
         if (!link) {
diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c
index 1a6eebe..c0f3df5 100644
--- a/src/network/networkd-netdev.c
+++ b/src/network/networkd-netdev.c
@@ -98,7 +98,7 @@ static int netdev_enslave_ready(Netdev *netdev, Link* link, sd_rtnl_message_hand
                 return r;
         }
 
-        r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->link->ifindex);
+        r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
         if (r < 0) {
                 log_error_netdev(netdev,
                                  "Could not append IFLA_MASTER attribute: %s",
@@ -140,7 +140,7 @@ static int netdev_enter_ready(Netdev *netdev) {
 
 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
         Netdev *netdev = userdata;
-        int r;
+        int r, ifindex;
 
         assert(netdev->state != _NETDEV_STATE_INVALID);
 
@@ -152,6 +152,14 @@ static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userda
                 return 1;
         }
 
+        r = sd_rtnl_message_link_get_ifindex(m, &ifindex);
+        if (r < 0)
+                log_warning_netdev(netdev, "created netdev with unknown ifindex: %s", strerror(-r));
+        else {
+                log_info_netdev(netdev, "created netdev with ifindex %d", ifindex);
+                netdev_set_ifindex(netdev, ifindex);
+        }
+
         return 1;
 }
 
@@ -288,21 +296,18 @@ int netdev_enslave(Netdev *netdev, Link *link, sd_rtnl_message_handler_t callbac
         return 0;
 }
 
-int netdev_set_link(Manager *m, NetdevKind kind, Link *link) {
-        Netdev *netdev;
-        int r;
-
-        r = netdev_get(m, link->ifname, &netdev);
-        if (r < 0)
-                return r;
-
-        if (netdev->link && netdev->link != link)
-                return -EEXIST;
+int netdev_set_ifindex(Netdev *netdev, int ifindex) {
+        assert(netdev);
+        assert(ifindex > 0);
 
-        if (netdev->kind != kind)
-                return -EINVAL;
+        if (netdev->ifindex > 0) {
+                if (netdev->ifindex == ifindex)
+                        return 0;
+                else
+                        return -EEXIST;
+        }
 
-        netdev->link = link;
+        netdev->ifindex = ifindex;
 
         netdev_enter_ready(netdev);
 
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 01a8a7f..831a9cb 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -77,7 +77,7 @@ struct Netdev {
 
         int vlanid;
 
-        Link *link;
+        int ifindex;
         NetdevState state;
 
         LIST_HEAD(netdev_enslave_callback, callbacks);
@@ -234,7 +234,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Netdev*, netdev_free);
 #define _cleanup_netdev_free_ _cleanup_(netdev_freep)
 
 int netdev_get(Manager *manager, const char *name, Netdev **ret);
-int netdev_set_link(Manager *m, NetdevKind kind, Link *link);
+int netdev_set_ifindex(Netdev *netdev, int ifindex);
 int netdev_enslave(Netdev *netdev, Link *link, sd_rtnl_message_handler_t cb);
 
 const char *netdev_kind_to_string(NetdevKind d) _const_;

commit 3815f36f05f8bc06904777b1eb7f1d22b78bcced
Author: Tom Gundersen <teg at jklm.no>
Date:   Wed Jan 29 21:20:30 2014 +0100

    sd-rtnl: beef up rtnl-util a bit

diff --git a/src/libsystemd/sd-rtnl/rtnl-internal.h b/src/libsystemd/sd-rtnl/rtnl-internal.h
index 5dc66ac..4bfcb83 100644
--- a/src/libsystemd/sd-rtnl/rtnl-internal.h
+++ b/src/libsystemd/sd-rtnl/rtnl-internal.h
@@ -29,6 +29,13 @@
 
 #include "sd-rtnl.h"
 
+#define RTNL_DEFAULT_TIMEOUT ((usec_t) (10 * USEC_PER_SEC))
+
+#define RTNL_WQUEUE_MAX 1024
+#define RTNL_RQUEUE_MAX 64*1024
+
+#define RTNL_CONTAINER_DEPTH 32
+
 struct reply_callback {
         sd_rtnl_message_handler_t callback;
         void *userdata;
@@ -78,20 +85,18 @@ struct sd_rtnl {
         sd_event *event;
 };
 
-#define RTNL_DEFAULT_TIMEOUT ((usec_t) (10 * USEC_PER_SEC))
-
-#define RTNL_WQUEUE_MAX 1024
-#define RTNL_RQUEUE_MAX 64*1024
+struct sd_rtnl_message {
+        RefCount n_ref;
 
-#define RTNL_CONTAINER_DEPTH 32
+        struct nlmsghdr *hdr;
+        size_t container_offsets[RTNL_CONTAINER_DEPTH]; /* offset from hdr to each container's start */
+        unsigned n_containers; /* number of containers */
+        size_t next_rta_offset; /* offset from hdr to next rta */
 
-int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret);
-uint32_t message_get_serial(sd_rtnl_message *m);
-int message_seal(sd_rtnl *nl, sd_rtnl_message *m);
+        bool sealed:1;
+};
 
-bool message_type_is_link(uint16_t type);
-bool message_type_is_addr(uint16_t type);
-bool message_type_is_route(uint16_t type);
+int message_new(sd_rtnl_message **ret, size_t initial_size);
 
 int socket_write_message(sd_rtnl *nl, sd_rtnl_message *m);
 int socket_read_message(sd_rtnl *nl, sd_rtnl_message **ret);
diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c
index 85c59bf..87ad682 100644
--- a/src/libsystemd/sd-rtnl/rtnl-message.c
+++ b/src/libsystemd/sd-rtnl/rtnl-message.c
@@ -29,25 +29,15 @@
 #include "refcnt.h"
 
 #include "sd-rtnl.h"
+#include "rtnl-util.h"
 #include "rtnl-internal.h"
 
-struct sd_rtnl_message {
-        RefCount n_ref;
-
-        struct nlmsghdr *hdr;
-        size_t container_offsets[RTNL_CONTAINER_DEPTH]; /* offset from hdr to each container's start */
-        unsigned n_containers; /* number of containers */
-        size_t next_rta_offset; /* offset from hdr to next rta */
-
-        bool sealed:1;
-};
-
 #define GET_CONTAINER(m, i) (i < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->container_offsets[i]) : NULL)
 #define NEXT_RTA(m) ((struct rtattr*)((uint8_t*)(m)->hdr + (m)->next_rta_offset))
 #define UPDATE_RTA(m, new) (m)->next_rta_offset = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
 #define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
 
-static int message_new(sd_rtnl_message **ret, size_t initial_size) {
+int message_new(sd_rtnl_message **ret, size_t initial_size) {
         sd_rtnl_message *m;
 
         assert_return(ret, -EINVAL);
@@ -73,67 +63,12 @@ static int message_new(sd_rtnl_message **ret, size_t initial_size) {
         return 0;
 }
 
-int message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret) {
-        struct nlmsgerr *err;
-        int r;
-
-        assert(error <= 0);
-
-        r = message_new(ret, NLMSG_SPACE(sizeof(struct nlmsgerr)));
-        if (r < 0)
-                return r;
-
-        (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
-        (*ret)->hdr->nlmsg_type = NLMSG_ERROR;
-        (*ret)->hdr->nlmsg_seq = serial;
-
-        err = NLMSG_DATA((*ret)->hdr);
-
-        err->error = error;
-
-        return 0;
-}
-
-bool message_type_is_route(uint16_t type) {
-        switch (type) {
-                case RTM_NEWROUTE:
-                case RTM_GETROUTE:
-                case RTM_DELROUTE:
-                        return true;
-                default:
-                        return false;
-        }
-}
-
-bool message_type_is_link(uint16_t type) {
-        switch (type) {
-                case RTM_NEWLINK:
-                case RTM_SETLINK:
-                case RTM_GETLINK:
-                case RTM_DELLINK:
-                        return true;
-                default:
-                        return false;
-        }
-}
-
-bool message_type_is_addr(uint16_t type) {
-        switch (type) {
-                case RTM_NEWADDR:
-                case RTM_GETADDR:
-                case RTM_DELADDR:
-                        return true;
-                default:
-                        return false;
-        }
-}
-
 int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) {
         struct rtmsg *rtm;
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
 
         rtm = NLMSG_DATA(m->hdr);
 
@@ -151,7 +86,7 @@ int sd_rtnl_message_route_new(uint16_t nlmsg_type, unsigned char rtm_family,
         struct rtmsg *rtm;
         int r;
 
-        assert_return(message_type_is_route(nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_route(nlmsg_type), -EINVAL);
         assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -182,7 +117,7 @@ int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags, unsigned
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
 
         ifi = NLMSG_DATA(m->hdr);
 
@@ -200,7 +135,7 @@ int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) {
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
 
         ifi = NLMSG_DATA(m->hdr);
 
@@ -213,7 +148,7 @@ int sd_rtnl_message_link_new(uint16_t nlmsg_type, int index, sd_rtnl_message **r
         struct ifinfomsg *ifi;
         int r;
 
-        assert_return(message_type_is_link(nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL);
         assert_return(nlmsg_type == RTM_NEWLINK || index > 0, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -241,7 +176,7 @@ int sd_rtnl_message_addr_set_prefixlen(sd_rtnl_message *m, unsigned char prefixl
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(message_type_is_addr(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL);
 
         ifa = NLMSG_DATA(m->hdr);
 
@@ -259,7 +194,7 @@ int sd_rtnl_message_addr_set_flags(sd_rtnl_message *m, unsigned char flags) {
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(message_type_is_addr(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL);
 
         ifa = NLMSG_DATA(m->hdr);
 
@@ -273,7 +208,7 @@ int sd_rtnl_message_addr_set_scope(sd_rtnl_message *m, unsigned char scope) {
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(message_type_is_addr(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_addr(m->hdr->nlmsg_type), -EINVAL);
 
         ifa = NLMSG_DATA(m->hdr);
 
@@ -287,7 +222,7 @@ int sd_rtnl_message_addr_new(uint16_t nlmsg_type, int index, unsigned char famil
         struct ifaddrmsg *ifa;
         int r;
 
-        assert_return(message_type_is_addr(nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_addr(nlmsg_type), -EINVAL);
         assert_return(index > 0, -EINVAL);
         assert_return(family == AF_INET || family == AF_INET6, -EINVAL);
         assert_return(ret, -EINVAL);
@@ -343,7 +278,7 @@ int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex) {
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
         assert_return(ifindex, -EINVAL);
 
         ifi = NLMSG_DATA(m->hdr);
@@ -358,7 +293,7 @@ int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags) {
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL);
         assert_return(flags, -EINVAL);
 
         ifi = NLMSG_DATA(m->hdr);
@@ -726,7 +661,7 @@ int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) {
 
         sd_rtnl_message_get_type(m, &rtm_type);
 
-        if (message_type_is_link(rtm_type)) {
+        if (rtnl_message_type_is_link(rtm_type)) {
                 if ((type == IFLA_LINKINFO && m->n_containers == 0) ||
                     (type == IFLA_INFO_DATA && m->n_containers == 1 &&
                      GET_CONTAINER(m, 0)->rta_type == IFLA_LINKINFO))
@@ -778,7 +713,7 @@ int sd_rtnl_message_read(sd_rtnl_message *m, unsigned short *type, void **data)
 
         *type = NEXT_RTA(m)->rta_type;
 
-        if (message_type_is_link(rtm_type) &&
+        if (rtnl_message_type_is_link(rtm_type) &&
             ((m->n_containers == 0 &&
               NEXT_RTA(m)->rta_type == IFLA_LINKINFO) ||
              (m->n_containers == 1 &&
@@ -805,7 +740,7 @@ int sd_rtnl_message_exit_container(sd_rtnl_message *m) {
         return 0;
 }
 
-uint32_t message_get_serial(sd_rtnl_message *m) {
+uint32_t rtnl_message_get_serial(sd_rtnl_message *m) {
         assert(m);
         assert(m->hdr);
 
@@ -826,7 +761,7 @@ int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
         return err->error;
 }
 
-int message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
+int rtnl_message_seal(sd_rtnl *nl, sd_rtnl_message *m) {
         int r;
 
         assert(m);
diff --git a/src/libsystemd/sd-rtnl/rtnl-util.c b/src/libsystemd/sd-rtnl/rtnl-util.c
index dfc0050..29250c9 100644
--- a/src/libsystemd/sd-rtnl/rtnl-util.c
+++ b/src/libsystemd/sd-rtnl/rtnl-util.c
@@ -25,6 +25,7 @@
 #include "sd-rtnl.h"
 
 #include "rtnl-util.h"
+#include "rtnl-internal.h"
 
 int rtnl_set_link_name(sd_rtnl *rtnl, int ifindex, const char *name) {
         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message = NULL;
@@ -98,3 +99,74 @@ int rtnl_set_link_properties(sd_rtnl *rtnl, int ifindex, const char *alias,
 
         return 0;
 }
+
+int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret) {
+        struct nlmsgerr *err;
+        int r;
+
+        assert(error <= 0);
+
+        r = message_new(ret, NLMSG_SPACE(sizeof(struct nlmsgerr)));
+        if (r < 0)
+                return r;
+
+        (*ret)->hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
+        (*ret)->hdr->nlmsg_type = NLMSG_ERROR;
+        (*ret)->hdr->nlmsg_seq = serial;
+
+        err = NLMSG_DATA((*ret)->hdr);
+
+        err->error = error;
+
+        return 0;
+}
+
+bool rtnl_message_type_is_route(uint16_t type) {
+        switch (type) {
+                case RTM_NEWROUTE:
+                case RTM_GETROUTE:
+                case RTM_DELROUTE:
+                        return true;
+                default:
+                        return false;
+        }
+}
+
+bool rtnl_message_type_is_link(uint16_t type) {
+        switch (type) {
+                case RTM_NEWLINK:
+                case RTM_SETLINK:
+                case RTM_GETLINK:
+                case RTM_DELLINK:
+                        return true;
+                default:
+                        return false;
+        }
+}
+
+bool rtnl_message_type_is_addr(uint16_t type) {
+        switch (type) {
+                case RTM_NEWADDR:
+                case RTM_GETADDR:
+                case RTM_DELADDR:
+                        return true;
+                default:
+                        return false;
+        }
+}
+
+int rtnl_message_link_get_ifname(sd_rtnl_message *message, const char **ret) {
+        unsigned short type;
+        void *name;
+
+        assert(rtnl_message_type_is_link(message->hdr->nlmsg_type));
+
+        while (sd_rtnl_message_read(message, &type, &name)) {
+                if (type == IFLA_IFNAME) {
+                        *ret = name;
+                        return 0;
+                }
+        }
+
+        return -ENOENT;
+}
diff --git a/src/libsystemd/sd-rtnl/rtnl-util.h b/src/libsystemd/sd-rtnl/rtnl-util.h
index 013002d..d590ca7 100644
--- a/src/libsystemd/sd-rtnl/rtnl-util.h
+++ b/src/libsystemd/sd-rtnl/rtnl-util.h
@@ -26,9 +26,19 @@
 #include "util.h"
 #include "sd-rtnl.h"
 
+int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message **ret);
+uint32_t rtnl_message_get_serial(sd_rtnl_message *m);
+int rtnl_message_seal(sd_rtnl *nl, sd_rtnl_message *m);
+
+bool rtnl_message_type_is_link(uint16_t type);
+bool rtnl_message_type_is_addr(uint16_t type);
+bool rtnl_message_type_is_route(uint16_t type);
+
 int rtnl_set_link_name(sd_rtnl *rtnl, int ifindex, const char *name);
 int rtnl_set_link_properties(sd_rtnl *rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu);
 
+int rtnl_message_link_get_ifname(sd_rtnl_message *m, const char **ret);
+
 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_rtnl*, sd_rtnl_unref);
 DEFINE_TRIVIAL_CLEANUP_FUNC(sd_rtnl_message*, sd_rtnl_message_unref);
 
diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c
index 08b82ab..04fcb3d 100644
--- a/src/libsystemd/sd-rtnl/sd-rtnl.c
+++ b/src/libsystemd/sd-rtnl/sd-rtnl.c
@@ -150,7 +150,7 @@ int sd_rtnl_send(sd_rtnl *nl,
         assert_return(!rtnl_pid_changed(nl), -ECHILD);
         assert_return(message, -EINVAL);
 
-        r = message_seal(nl, message);
+        r = rtnl_message_seal(nl, message);
         if (r < 0)
                 return r;
 
@@ -181,7 +181,7 @@ int sd_rtnl_send(sd_rtnl *nl,
         }
 
         if (serial)
-                *serial = message_get_serial(message);
+                *serial = rtnl_message_get_serial(message);
 
         return 1;
 }
@@ -256,7 +256,7 @@ static int process_timeout(sd_rtnl *rtnl) {
         if (c->timeout > n)
                 return 0;
 
-        r = message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
+        r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m);
         if (r < 0)
                 return r;
 
@@ -277,7 +277,7 @@ static int process_reply(sd_rtnl *rtnl, sd_rtnl_message *m) {
         assert(rtnl);
         assert(m);
 
-        serial = message_get_serial(m);
+        serial = rtnl_message_get_serial(m);
         c = hashmap_remove(rtnl->reply_callbacks, &serial);
         if (!c)
                 return 0;
@@ -580,7 +580,7 @@ int sd_rtnl_call(sd_rtnl *nl,
                 if (r < 0)
                         return r;
                 if (incoming) {
-                        uint32_t received_serial = message_get_serial(incoming);
+                        uint32_t received_serial = rtnl_message_get_serial(incoming);
 
                         if (received_serial == serial) {
                                 r = sd_rtnl_message_get_errno(incoming);
@@ -833,7 +833,9 @@ int sd_rtnl_add_match(sd_rtnl *rtnl,
         assert_return(rtnl, -EINVAL);
         assert_return(callback, -EINVAL);
         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
-        assert_return(message_type_is_link(type) || message_type_is_addr(type) || message_type_is_route(type), -ENOTSUP);
+        assert_return(rtnl_message_type_is_link(type) ||
+                      rtnl_message_type_is_addr(type) ||
+                      rtnl_message_type_is_route(type), -ENOTSUP);
 
         c = new0(struct match_callback, 1);
         if (!c)
diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c
index ec6b294..15e42b6 100644
--- a/src/libsystemd/sd-rtnl/test-rtnl.c
+++ b/src/libsystemd/sd-rtnl/test-rtnl.c
@@ -27,7 +27,6 @@
 #include "sd-rtnl.h"
 #include "socket-util.h"
 #include "rtnl-util.h"
-#include "rtnl-internal.h"
 #include "event-util.h"
 
 static void test_link_configure(sd_rtnl *rtnl, int ifindex) {
@@ -86,7 +85,7 @@ static void test_route(void) {
                 return;
         }
 
-        assert(message_seal(NULL, req) >= 0);
+        assert(rtnl_message_seal(NULL, req) >= 0);
 
         assert(sd_rtnl_message_read(req, &type, &data) > 0);
         assert(type == RTA_GATEWAY);
@@ -227,7 +226,7 @@ static void test_container(void) {
         assert(sd_rtnl_message_close_container(m) >= 0);
         assert(sd_rtnl_message_close_container(m) == -EINVAL);
 
-        assert(message_seal(NULL, m) >= 0);
+        assert(rtnl_message_seal(NULL, m) >= 0);
 
         assert(sd_rtnl_message_read(m, &type, &data) >= 0);
         assert(type == IFLA_LINKINFO);

commit 377a218f876507fb8be9c21ef4121fa2576ec317
Author: Tom Gundersen <teg at jklm.no>
Date:   Tue Jan 28 23:23:31 2014 +0100

    sd-dhcp-client/net-util: make netmask_to_prefixlen generic
    
    This was originally included in the dhcp-client at my request, but it is not
    really dhcp-specific and useful outside of it, so let's pull it out.

diff --git a/src/libsystemd-dhcp/sd-dhcp-client.c b/src/libsystemd-dhcp/sd-dhcp-client.c
index 3b7b9f4..7e5c36a 100644
--- a/src/libsystemd-dhcp/sd-dhcp-client.c
+++ b/src/libsystemd-dhcp/sd-dhcp-client.c
@@ -240,21 +240,6 @@ int sd_dhcp_client_get_hostname(sd_dhcp_client *client, const char **hostname) {
         return 0;
 }
 
-int sd_dhcp_client_prefixlen(const struct in_addr *addr) {
-        int len = 0;
-        uint32_t mask;
-
-        assert_return(addr, -EADDRNOTAVAIL);
-
-        mask = be32toh(addr->s_addr);
-        while (mask) {
-                len++;
-                mask = mask << 1;
-        }
-
-        return len;
-}
-
 int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr) {
         assert_return(client, -EINVAL);
         assert_return(addr, -EINVAL);
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index dec33e8..f021918 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -26,6 +26,7 @@
 #include "libudev-private.h"
 #include "util.h"
 #include "bus-util.h"
+#include "net-util.h"
 
 int link_new(Manager *manager, struct udev_device *device, Link **ret) {
         _cleanup_link_free_ Link *link = NULL;
@@ -422,7 +423,7 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
         struct in_addr address;
         struct in_addr netmask;
         struct in_addr gateway;
-        int prefixlen;
+        unsigned prefixlen;
         int r;
 
         assert(link);
@@ -496,12 +497,7 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
                 return;
         }
 
-        prefixlen = sd_dhcp_client_prefixlen(&netmask);
-        if (prefixlen < 0) {
-                log_warning_link(link, "DHCP error: no prefixlen");
-                link_enter_failed(link);
-                return;
-        }
+        prefixlen = net_netmask_to_prefixlen(&netmask);
 
         r = sd_dhcp_client_get_router(client, &gateway);
         if (r < 0) {
diff --git a/src/shared/net-util.c b/src/shared/net-util.c
index 887dae5..630be18 100644
--- a/src/shared/net-util.c
+++ b/src/shared/net-util.c
@@ -58,6 +58,21 @@ bool net_match_config(const struct ether_addr *match_mac,
         return 1;
 }
 
+unsigned net_netmask_to_prefixlen(const struct in_addr *addr) {
+        unsigned len = 0;
+        uint32_t mask;
+
+        assert(addr);
+
+        mask = be32toh(addr->s_addr);
+        while (mask) {
+                len++;
+                mask = mask << 1;
+        }
+
+        return len;
+}
+
 int config_parse_ifname(const char *unit,
                         const char *filename,
                         unsigned line,
diff --git a/src/shared/net-util.h b/src/shared/net-util.h
index c7edfb9..0ec04db 100644
--- a/src/shared/net-util.h
+++ b/src/shared/net-util.h
@@ -22,6 +22,7 @@
 #pragma once
 
 #include <netinet/ether.h>
+#include <netinet/in.h>
 #include <stdbool.h>
 
 bool net_match_config(const struct ether_addr *match_mac,
@@ -35,6 +36,8 @@ bool net_match_config(const struct ether_addr *match_mac,
                       const char *dev_type,
                       const char *dev_name);
 
+unsigned net_netmask_to_prefixlen(const struct in_addr *netmask);
+
 int config_parse_hwaddr(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);
diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h
index 0f16e99..e663611 100644
--- a/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/sd-dhcp-client.h
@@ -52,7 +52,6 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client,
 
 int sd_dhcp_client_get_address(sd_dhcp_client *client, struct in_addr *addr);
 int sd_dhcp_client_get_netmask(sd_dhcp_client *client, struct in_addr *addr);
-int sd_dhcp_client_prefixlen(const struct in_addr *addr);
 int sd_dhcp_client_get_router(sd_dhcp_client *client, struct in_addr *addr);
 int sd_dhcp_client_get_dns(sd_dhcp_client *client, struct in_addr **addr, size_t *addr_size);
 int sd_dhcp_client_get_mtu(sd_dhcp_client *client, uint16_t *mtu);

commit eb0ea358b688a6f83ff305c6b825c61f12b6dcb8
Author: Tom Gundersen <teg at jklm.no>
Date:   Tue Jan 28 20:01:37 2014 +0100

    networkd: address - add support for broadcast

diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index de5566d..aa7927f 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -186,11 +186,7 @@ int address_configure(Address *address, Link *link,
         }
 
         if (address->family == AF_INET) {
-                struct in_addr broadcast;
-
-                broadcast.s_addr = address->in_addr.in.s_addr | address->netmask.s_addr;
-
-                r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &broadcast);
+                r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast);
                 if (r < 0) {
                         log_error("Could not append IFA_BROADCAST attribute: %s",
                                   strerror(-r));
@@ -253,6 +249,43 @@ int config_parse_dns(const char *unit,
         return 0;
 }
 
+int config_parse_broadcast(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) {
+        Network *network = userdata;
+        _cleanup_address_free_ Address *n = NULL;
+        _cleanup_free_ char *address = NULL;
+        int r;
+
+        assert(filename);
+        assert(section);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        r = address_new_static(network, section_line, &n);
+        if (r < 0)
+                return r;
+
+        r = net_parse_inaddr(address, &n->family, &n->broadcast);
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Broadcast is invalid, ignoring assignment: %s", address);
+                return 0;
+        }
+
+        n = NULL;
+
+        return 0;
+}
+
 int config_parse_address(const char *unit,
                 const char *filename,
                 unsigned line,
@@ -300,7 +333,6 @@ int config_parse_address(const char *unit,
                 }
 
                 n->prefixlen = (unsigned char) i;
-                n->netmask.s_addr = htonl(0xfffffffflu >> n->prefixlen);
 
                 address = strndup(rvalue, e - rvalue);
                 if (!address)
@@ -318,6 +350,10 @@ int config_parse_address(const char *unit,
                 return 0;
         }
 
+        if (n->family == AF_INET && !n->broadcast.s_addr)
+                n->broadcast.s_addr = n->in_addr.in.s_addr |
+                                      htonl(0xfffffffflu >> n->prefixlen);
+
         n = NULL;
 
         return 0;
diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf
index de72805..64dab7c 100644
--- a/src/network/networkd-gperf.gperf
+++ b/src/network/networkd-gperf.gperf
@@ -29,6 +29,7 @@ Network.Address,           config_parse_address,          0,       0
 Network.Gateway,           config_parse_gateway,          0,       0
 Network.DNS,               config_parse_dns,              0,       offsetof(Network, dns)
 Address.Address,           config_parse_address,          0,       0
+Address.Broadcast,         config_parse_broadcast,        0,       0
 Address.Label,             config_parse_label,            0,       0
 Route.Gateway,             config_parse_gateway,          0,       0
 Route.Destination,         config_parse_destination,      0,       0
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 9b5ebed..dec33e8 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -540,7 +540,7 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
                 addr->family = AF_INET;
                 addr->in_addr.in = address;
                 addr->prefixlen = prefixlen;
-                addr->netmask = netmask;
+                addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr;
 
                 r = route_new_dynamic(&rt);
                 if (r < 0) {
diff --git a/src/network/networkd.h b/src/network/networkd.h
index f174abb..01a8a7f 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -123,7 +123,7 @@ struct Address {
         unsigned char prefixlen;
         char *label;
 
-        struct in_addr netmask;
+        struct in_addr broadcast;
 
         union {
                 struct in_addr in;
@@ -305,6 +305,10 @@ int config_parse_address(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_broadcast(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_label(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);

commit 801bd9e859d7f3f127617172910786929776472b
Author: Tom Gundersen <teg at jklm.no>
Date:   Tue Jan 28 20:00:47 2014 +0100

    net-util: verify the address family
    
    Error out if the address family is already set to something incompatible with the
    address being parsed.

diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 3f78794..de5566d 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -46,6 +46,8 @@ int address_new_static(Network *network, unsigned section, Address **ret) {
         if (!address)
                 return -ENOMEM;
 
+        address->family = AF_UNSPEC;
+
         address->network = network;
 
         LIST_PREPEND(static_addresses, network->static_addresses, address);
@@ -68,6 +70,8 @@ int address_new_dynamic(Address **ret) {
         if (!address)
                 return -ENOMEM;
 
+        address->family = AF_UNSPEC;
+
         *ret = address;
         address = NULL;
 
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 2a72034..94138cd 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -84,12 +84,6 @@ static int network_load_one(Manager *manager, const char *filename) {
                                     "Ignoring", filename);
                         return 0;
                 }
-
-                if (route->dst_family && route->family != route->dst_family) {
-                        log_warning("Route section with conflicting Gateway and Destination address "
-                                    "family configured in %s. Ignoring", filename);
-                        return 0;
-                }
         }
 
         LIST_FOREACH(static_addresses, address, network->static_addresses) {
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index 83dad65..29ee104 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -47,6 +47,8 @@ int route_new_static(Network *network, unsigned section, Route **ret) {
         if (!route)
                 return -ENOMEM;
 
+        route->family = AF_UNSPEC;
+
         route->network = network;
 
         LIST_PREPEND(static_routes, network->static_routes, route);
@@ -69,6 +71,8 @@ int route_new_dynamic(Route **ret) {
         if (!route)
                 return -ENOMEM;
 
+        route->family = AF_UNSPEC;
+
         *ret = route;
         route = NULL;
 
@@ -231,7 +235,7 @@ int config_parse_destination(const char *unit,
                         return log_oom();
         }
 
-        r = net_parse_inaddr(address, &n->dst_family, &n->dst_addr);
+        r = net_parse_inaddr(address, &n->family, &n->dst_addr);
         if (r < 0) {
                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
                            "Destination is invalid, ignoring assignment: %s", address);
@@ -252,7 +256,7 @@ int config_parse_destination(const char *unit,
 
                 n->dst_prefixlen = (unsigned char) i;
         } else {
-                switch (n->dst_family) {
+                switch (n->family) {
                         case AF_INET:
                                 n->dst_prefixlen = 32;
                                 break;
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 968edf6..f174abb 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -138,7 +138,6 @@ struct Route {
         uint64_t section;
 
         unsigned char family;
-        unsigned char dst_family;
         unsigned char dst_prefixlen;
 
         union {
diff --git a/src/shared/net-util.c b/src/shared/net-util.c
index 8f8cfc9..887dae5 100644
--- a/src/shared/net-util.c
+++ b/src/shared/net-util.c
@@ -192,16 +192,24 @@ int net_parse_inaddr(const char *address, unsigned char *family, void *dst) {
 
         /* IPv4 */
         r = inet_pton(AF_INET, address, dst);
-        if (r > 0)
-                *family = AF_INET; /* successfully parsed IPv4 address */
-        else  if (r < 0)
+        if (r > 0) {
+                /* succsefully parsed IPv4 address */
+                if (*family == AF_UNSPEC)
+                        *family = AF_INET;
+                else if (*family != AF_INET)
+                        return -EINVAL;
+        } else  if (r < 0)
                 return -errno;
         else {
                 /* not an IPv4 address, so let's try IPv6 */
                 r = inet_pton(AF_INET6, address, dst);
-                if (r > 0)
-                        *family = AF_INET6; /* successfully parsed IPv6 address */
-                else if (r < 0)
+                if (r > 0) {
+                        /* successfully parsed IPv6 address */
+                        if (*family == AF_UNSPEC)
+                                *family = AF_INET6;
+                        else if (*family != AF_INET6)
+                                return -EINVAL;
+                } else if (r < 0)
                         return -errno;
                 else
                         return -EINVAL;

commit eb27aeca247a4cf8816fffc4c0dbcab55ead3864
Author: Tom Gundersen <teg at jklm.no>
Date:   Mon Jan 27 20:52:07 2014 +0100

    networkd: dhcpv4 - add notion of 'CriticalConnection'
    
    These connections are never torn down, even when the DHCP specifications say that
    they should be. This is useful/necessary when the rootfs (or another critical fs)
    is mounted over this network connection, and dataloss would result if the connection
    is lost.
    
    This option defaults to off, but our initrd generator (TBD) will enable it when
    applicable.

diff --git a/man/systemd-networkd.service.xml b/man/systemd-networkd.service.xml
index 3403a7a..3e6fddc 100644
--- a/man/systemd-networkd.service.xml
+++ b/man/systemd-networkd.service.xml
@@ -293,6 +293,14 @@
                                                 will be used as the transient hostname.</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
+                                                expires. This is contrary to the DHCP specification, but may be the best choice
+                                                if, say, the root filesystem relies on this connection. Defaults to false.</para>
+                                        </listitem>
+                                </varlistentry>
                        </variablelist>
 
                 </refsect2>
diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf
index aff23bb..de72805 100644
--- a/src/network/networkd-gperf.gperf
+++ b/src/network/networkd-gperf.gperf
@@ -15,28 +15,29 @@ struct ConfigPerfItem;
 %struct-type
 %includes
 %%
-Match.MACAddress,        config_parse_hwaddr,           0,       offsetof(Network, match_mac)
-Match.Path,              config_parse_string,           0,       offsetof(Network, match_path)
-Match.Driver,            config_parse_string,           0,       offsetof(Network, match_driver)
-Match.Type,              config_parse_string,           0,       offsetof(Network, match_type)
-Match.Name,              config_parse_ifname,           0,       offsetof(Network, match_name)
-Network.Description,     config_parse_string,           0,       offsetof(Network, description)
-Network.Bridge,          config_parse_bridge,           0,       offsetof(Network, bridge)
-Network.Bond,            config_parse_bond,             0,       offsetof(Network, bond)
-Network.VLAN,            config_parse_vlan,             0,       offsetof(Network, vlan)
-Network.DHCP,            config_parse_bool,             0,       offsetof(Network, dhcp)
-Network.Address,         config_parse_address,          0,       0
-Network.Gateway,         config_parse_gateway,          0,       0
-Network.DNS,             config_parse_dns,              0,       offsetof(Network, dns)
-Address.Address,         config_parse_address,          0,       0
-Address.Label,           config_parse_label,            0,       0
-Route.Gateway,           config_parse_gateway,          0,       0
-Route.Destination,       config_parse_destination,      0,       0
-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)
-DHCPv4.UseDomainName,    config_parse_bool,             0,       offsetof(Network, dhcp_domainname)
-Netdev.Description,      config_parse_string,           0,       offsetof(Netdev, description)
-Netdev.Name,             config_parse_ifname,           0,       offsetof(Netdev, name)
-Netdev.Kind,             config_parse_netdev_kind,      0,       offsetof(Netdev, kind)
-VLAN.Id,                 config_parse_int,              0,       offsetof(Netdev, vlanid)
+Match.MACAddress,          config_parse_hwaddr,           0,       offsetof(Network, match_mac)
+Match.Path,                config_parse_string,           0,       offsetof(Network, match_path)
+Match.Driver,              config_parse_string,           0,       offsetof(Network, match_driver)
+Match.Type,                config_parse_string,           0,       offsetof(Network, match_type)
+Match.Name,                config_parse_ifname,           0,       offsetof(Network, match_name)
+Network.Description,       config_parse_string,           0,       offsetof(Network, description)
+Network.Bridge,            config_parse_bridge,           0,       offsetof(Network, bridge)
+Network.Bond,              config_parse_bond,             0,       offsetof(Network, bond)
+Network.VLAN,              config_parse_vlan,             0,       offsetof(Network, vlan)
+Network.DHCP,              config_parse_bool,             0,       offsetof(Network, dhcp)
+Network.Address,           config_parse_address,          0,       0
+Network.Gateway,           config_parse_gateway,          0,       0
+Network.DNS,               config_parse_dns,              0,       offsetof(Network, dns)
+Address.Address,           config_parse_address,          0,       0
+Address.Label,             config_parse_label,            0,       0
+Route.Gateway,             config_parse_gateway,          0,       0
+Route.Destination,         config_parse_destination,      0,       0
+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)
+DHCPv4.UseDomainName,      config_parse_bool,             0,       offsetof(Network, dhcp_domainname)
+DHCPv4.CriticalConnection, config_parse_bool,             0,       offsetof(Network, dhcp_critical)
+Netdev.Description,        config_parse_string,           0,       offsetof(Netdev, description)
+Netdev.Name,               config_parse_ifname,           0,       offsetof(Netdev, name)
+Netdev.Kind,               config_parse_netdev_kind,      0,       offsetof(Netdev, kind)
+VLAN.Id,                   config_parse_int,              0,       offsetof(Netdev, vlanid)
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index d9622ac..9b5ebed 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -443,6 +443,12 @@ static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
 
         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
             event == DHCP_EVENT_STOP) {
+                if (link->network->dhcp_critical) {
+                        log_warning_link(link, "DHCPv4 connection considered system critical, "
+                                         "ignoring request to reconfigure it down.");
+                        return;
+                }
+
                 if (link->dhcp_address) {
                         address_drop(link->dhcp_address, link, address_drop_handler);
 
diff --git a/src/network/networkd.h b/src/network/networkd.h
index 22dcbd0..968edf6 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -103,6 +103,7 @@ struct Network {
         bool dhcp_mtu;
         bool dhcp_hostname;
         bool dhcp_domainname;
+        bool dhcp_critical;
 
         LIST_HEAD(Address, static_addresses);
         LIST_HEAD(Route, static_routes);



More information about the systemd-commits mailing list