chiark / gitweb /
networkd: dhcp-server - start as soon as addresses have been set
[elogind.git] / src / network / networkd-link.c
index 63c7a8bf248195f1aad1dd7298a7d6464925a4c7..acdb486381fe8a3a97f767969c8178846672ecdf 100644 (file)
 #include <linux/if.h>
 #include <unistd.h>
 
-#include "networkd-link.h"
-#include "networkd-netdev.h"
-#include "libudev-private.h"
-#include "udev-util.h"
 #include "util.h"
 #include "virt.h"
+#include "fileio.h"
 #include "bus-util.h"
+#include "udev-util.h"
+#include "libudev-private.h"
 #include "network-internal.h"
+#include "networkd-link.h"
+#include "networkd-netdev.h"
 #include "conf-parser.h"
-
 #include "dhcp-lease-internal.h"
 
 static bool link_dhcp6_enabled(Link *link) {
@@ -42,7 +42,7 @@ static bool link_dhcp6_enabled(Link *link) {
         if (!link->network)
                 return false;
 
-        return IN_SET(link->network->dhcp, DHCP_SUPPORT_V6, DHCP_SUPPORT_BOTH);
+        return IN_SET(link->network->dhcp, ADDRESS_FAMILY_IPV6, ADDRESS_FAMILY_YES);
 }
 
 static bool link_dhcp4_enabled(Link *link) {
@@ -52,7 +52,7 @@ static bool link_dhcp4_enabled(Link *link) {
         if (!link->network)
                 return false;
 
-        return IN_SET(link->network->dhcp, DHCP_SUPPORT_V4, DHCP_SUPPORT_BOTH);
+        return IN_SET(link->network->dhcp, ADDRESS_FAMILY_IPV4, ADDRESS_FAMILY_YES);
 }
 
 static bool link_dhcp4_server_enabled(Link *link) {
@@ -82,12 +82,32 @@ static bool link_lldp_enabled(Link *link) {
         if (!link->network)
                 return false;
 
-        if(link->network->bridge)
+        if (link->network->bridge)
                 return false;
 
         return link->network->lldp;
 }
 
+static bool link_ipv4_forward_enabled(Link *link) {
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        if (!link->network)
+                return false;
+
+        return IN_SET(link->network->ip_forward, ADDRESS_FAMILY_IPV4, ADDRESS_FAMILY_YES);
+}
+
+static bool link_ipv6_forward_enabled(Link *link) {
+        if (link->flags & IFF_LOOPBACK)
+                return false;
+
+        if (!link->network)
+                return false;
+
+        return IN_SET(link->network->ip_forward, ADDRESS_FAMILY_IPV6, ADDRESS_FAMILY_YES);
+}
+
 #define FLAG_STRING(string, flag, old, new) \
         (((old ^ new) & flag) \
                 ? ((old & flag) ? (" -" string) : (" +" string)) \
@@ -266,6 +286,8 @@ static void link_free(Link *link) {
         unlink(link->lease_file);
         free(link->lease_file);
 
+        sd_lldp_free(link->lldp);
+
         unlink(link->lldp_file);
         free(link->lldp_file);
 
@@ -421,7 +443,7 @@ static Address* link_find_dhcp_server_address(Link *link) {
         assert(link);
         assert(link->network);
 
-        /* The the first statically configured address if there is any */
+        /* The first statically configured address if there is any */
         LIST_FOREACH(addresses, address, link->network->static_addresses) {
 
                 if (address->family != AF_INET)
@@ -445,63 +467,10 @@ static Address* link_find_dhcp_server_address(Link *link) {
 }
 
 static int link_enter_configured(Link *link) {
-        int r;
-
         assert(link);
         assert(link->network);
         assert(link->state == LINK_STATE_SETTING_ROUTES);
 
-        if (link_dhcp4_server_enabled(link) &&
-            !sd_dhcp_server_is_running(link->dhcp_server)) {
-                struct in_addr pool_start;
-                Address *address;
-
-                address = link_find_dhcp_server_address(link);
-                if (!address) {
-                        log_link_warning(link,
-                                         "Failed to find suitable address for DHCPv4 server instance.");
-                        link_enter_failed(link);
-                        return 0;
-                }
-
-                log_link_debug(link, "offering DHCPv4 leases");
-
-                r = sd_dhcp_server_set_address(link->dhcp_server,
-                                               &address->in_addr.in,
-                                               address->prefixlen);
-                if (r < 0)
-                        return r;
-
-                /* offer 32 addresses starting from the address following the server address */
-                pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1);
-                r = sd_dhcp_server_set_lease_pool(link->dhcp_server,
-                                                  &pool_start, 32);
-                if (r < 0)
-                        return r;
-
-                /* TODO:
-                r = sd_dhcp_server_set_router(link->dhcp_server,
-                                              &main_address->in_addr.in);
-                if (r < 0)
-                        return r;
-
-                r = sd_dhcp_server_set_prefixlen(link->dhcp_server,
-                                                 main_address->prefixlen);
-                if (r < 0)
-                        return r;
-                */
-
-                r = sd_dhcp_server_start(link->dhcp_server);
-                if (r < 0) {
-                        log_link_warning(link, "could not start DHCPv4 server "
-                                         "instance: %s", strerror(-r));
-
-                        link_enter_failed(link);
-
-                        return 0;
-                }
-        }
-
         log_link_info(link, "link configured");
 
         link->state = LINK_STATE_CONFIGURED;
@@ -653,9 +622,7 @@ static int link_enter_set_addresses(Link *link) {
         LIST_FOREACH(addresses, ad, link->network->static_addresses) {
                 r = address_configure(ad, link, &address_handler);
                 if (r < 0) {
-                        log_link_warning(link,
-                                         "could not set addresses: %s",
-                                         strerror(-r));
+                        log_link_warning_errno(link, r, "Could not set addresses: %m");
                         link_enter_failed(link);
                         return r;
                 }
@@ -663,6 +630,58 @@ static int link_enter_set_addresses(Link *link) {
                 link->link_messages ++;
         }
 
+        /* now that we can figure out a default address for the dhcp server,
+           start it */
+        if (link_dhcp4_server_enabled(link)) {
+                struct in_addr pool_start;
+                Address *address;
+
+                address = link_find_dhcp_server_address(link);
+                if (!address) {
+                        log_link_warning(link,
+                                         "Failed to find suitable address for DHCPv4 server instance.");
+                        link_enter_failed(link);
+                        return 0;
+                }
+
+                r = sd_dhcp_server_set_address(link->dhcp_server,
+                                               &address->in_addr.in,
+                                               address->prefixlen);
+                if (r < 0)
+                        return r;
+
+                /* offer 32 addresses starting from the address following the server address */
+                pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1);
+                r = sd_dhcp_server_set_lease_pool(link->dhcp_server,
+                                                  &pool_start, 32);
+                if (r < 0)
+                        return r;
+
+                /* TODO:
+                r = sd_dhcp_server_set_router(link->dhcp_server,
+                                              &main_address->in_addr.in);
+                if (r < 0)
+                        return r;
+
+                r = sd_dhcp_server_set_prefixlen(link->dhcp_server,
+                                                 main_address->prefixlen);
+                if (r < 0)
+                        return r;
+                */
+
+                r = sd_dhcp_server_start(link->dhcp_server);
+                if (r < 0) {
+                        log_link_warning(link, "could not start DHCPv4 server "
+                                         "instance: %s", strerror(-r));
+
+                        link_enter_failed(link);
+
+                        return 0;
+                }
+
+                log_link_debug(link, "offering DHCPv4 leases");
+        }
+
         if (link->link_messages == 0) {
                 link_enter_set_routes(link);
         } else
@@ -1217,6 +1236,56 @@ static int link_enter_join_netdev(Link *link) {
         return 0;
 }
 
+static int link_set_ipv4_forward(Link *link) {
+        const char *p = NULL;
+        bool b;
+        int r;
+
+        b = link_ipv4_forward_enabled(link);
+
+        p = strappenda("/proc/sys/net/ipv4/conf/", link->ifname, "/forwarding");
+        r = write_string_file_no_create(p, one_zero(b));
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot configure IPv4 forwarding for interface %s: %m", link->ifname);
+
+        if (b) {
+                _cleanup_free_ char *buf = NULL;
+
+                /* If IP forwarding is turned on for this interface,
+                 * then propagate this to the global setting. Given
+                 * that turning this on has side-effects on other
+                 * fields, we'll try to avoid doing this unless
+                 * necessary, hence check the previous value
+                 * first. Note that we never turn this option off
+                 * again, since all interfaces we manage do not do
+                 * forwarding anyway by default, and ownership rules
+                 * of this control are so unclear. */
+
+                r = read_one_line_file("/proc/sys/net/ipv4/ip_forward", &buf);
+                if (r < 0)
+                        log_link_warning_errno(link, r, "Cannot read /proc/sys/net/ipv4/ip_forward: %m");
+                else if (!streq(buf, "1")) {
+                        r = write_string_file_no_create("/proc/sys/net/ipv4/ip_forward", "1");
+                        if (r < 0)
+                                log_link_warning_errno(link, r, "Cannot write /proc/sys/net/ipv4/ip_forward: %m");
+                }
+        }
+
+        return 0;
+}
+
+static int link_set_ipv6_forward(Link *link) {
+        const char *p = NULL;
+        int r;
+
+        p = strappenda("/proc/sys/net/ipv6/conf/", link->ifname, "/forwarding");
+        r = write_string_file_no_create(p, one_zero(link_ipv6_forward_enabled(link)));
+        if (r < 0)
+                log_link_warning_errno(link, r, "Cannot configure IPv6 forwarding for interface: %m");
+
+        return 0;
+}
+
 static int link_configure(Link *link) {
         int r;
 
@@ -1228,6 +1297,14 @@ static int link_configure(Link *link) {
         if (r < 0)
                 return r;
 
+        r = link_set_ipv4_forward(link);
+        if (r < 0)
+                return r;
+
+        r = link_set_ipv6_forward(link);
+        if (r < 0)
+                return r;
+
         if (link_ipv4ll_enabled(link)) {
                 r = ipv4ll_configure(link);
                 if (r < 0)
@@ -1307,7 +1384,7 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m,
                 if (network->ipv4ll)
                         log_link_debug(link, "ignoring IPv4LL for loopback link");
 
-                if (network->dhcp != DHCP_SUPPORT_NONE)
+                if (network->dhcp != ADDRESS_FAMILY_NO)
                         log_link_debug(link, "ignoring DHCP clients for loopback link");
 
                 if (network->dhcp_server)
@@ -1364,16 +1441,27 @@ int link_initialized(Link *link, struct udev_device *device) {
         return 0;
 }
 
+static Address* link_get_equal_address(Link *link, Address *needle) {
+        Address *i;
+
+        assert(link);
+        assert(needle);
+
+        LIST_FOREACH(addresses, i, link->addresses)
+                if (address_equal(i, needle))
+                        return i;
+
+        return NULL;
+}
+
 int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) {
         Manager *m = userdata;
         Link *link = NULL;
         uint16_t type;
         _cleanup_address_free_ Address *address = NULL;
-        Address *ad;
-        char buf[INET6_ADDRSTRLEN];
-        char valid_buf[FORMAT_TIMESPAN_MAX];
+        Address *existing;
+        char buf[INET6_ADDRSTRLEN], valid_buf[FORMAT_TIMESPAN_MAX];
         const char *valid_str = NULL;
-        bool address_dropped = false;
         int r, ifindex;
 
         assert(rtnl);
@@ -1415,50 +1503,42 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use
 
         r = sd_rtnl_message_addr_get_family(message, &address->family);
         if (r < 0 || !IN_SET(address->family, AF_INET, AF_INET6)) {
-                log_link_warning(link,
-                                 "rtnl: received address with invalid family, ignoring");
+                log_link_warning(link, "rtnl: received address with invalid family, ignoring");
                 return 0;
         }
 
         r = sd_rtnl_message_addr_get_prefixlen(message, &address->prefixlen);
         if (r < 0) {
-                log_link_warning(link,
-                                 "rtnl: received address with invalid prefixlen, ignoring");
+                log_link_warning(link, "rtnl: received address with invalid prefixlen, ignoring");
                 return 0;
         }
 
         r = sd_rtnl_message_addr_get_scope(message, &address->scope);
         if (r < 0) {
-                log_link_warning(link,
-                                 "rtnl: received address with invalid scope, ignoring");
+                log_link_warning(link, "rtnl: received address with invalid scope, ignoring");
                 return 0;
         }
 
         r = sd_rtnl_message_addr_get_flags(message, &address->flags);
         if (r < 0) {
-                log_link_warning(link,
-                                 "rtnl: received address with invalid flags, ignoring");
+                log_link_warning(link, "rtnl: received address with invalid flags, ignoring");
                 return 0;
         }
 
         switch (address->family) {
         case AF_INET:
-                r = sd_rtnl_message_read_in_addr(message, IFA_LOCAL,
-                                                 &address->in_addr.in);
+                r = sd_rtnl_message_read_in_addr(message, IFA_LOCAL, &address->in_addr.in);
                 if (r < 0) {
-                        log_link_warning(link,
-                                         "rtnl: received address without valid address, ignoring");
+                        log_link_warning(link, "rtnl: received address without valid address, ignoring");
                         return 0;
                 }
 
                 break;
 
         case AF_INET6:
-                r = sd_rtnl_message_read_in6_addr(message, IFA_ADDRESS,
-                                                  &address->in_addr.in6);
+                r = sd_rtnl_message_read_in6_addr(message, IFA_ADDRESS, &address->in_addr.in6);
                 if (r < 0) {
-                        log_link_warning(link,
-                                         "rtnl: received address without valid address, ignoring");
+                        log_link_warning(link, "rtnl: received address without valid address, ignoring");
                         return 0;
                 }
 
@@ -1468,14 +1548,12 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use
                 assert_not_reached("invalid address family");
         }
 
-        if (!inet_ntop(address->family, &address->in_addr, buf,
-                       INET6_ADDRSTRLEN)) {
+        if (!inet_ntop(address->family, &address->in_addr, buf, INET6_ADDRSTRLEN)) {
                 log_link_warning(link, "could not print address");
                 return 0;
         }
 
-        r = sd_rtnl_message_read_cache_info(message, IFA_CACHEINFO,
-                                            &address->cinfo);
+        r = sd_rtnl_message_read_cache_info(message, IFA_CACHEINFO, &address->cinfo);
         if (r >= 0) {
                 if (address->cinfo.ifa_valid == CACHE_INFO_INFINITY_LIFE_TIME)
                         valid_str = "ever";
@@ -1485,43 +1563,40 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *use
                                                     USEC_PER_SEC);
         }
 
-        LIST_FOREACH(addresses, ad, link->addresses) {
-                if (address_equal(ad, address)) {
-                        LIST_REMOVE(addresses, link->addresses, ad);
+        existing = link_get_equal_address(link, address);
 
-                        address_free(ad);
+        switch (type) {
+        case RTM_NEWADDR:
+                if (existing) {
+                        log_link_debug(link, "Updating address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
 
-                        address_dropped = true;
 
-                        break;
-                }
-        }
+                        existing->scope = address->scope;
+                        existing->flags = address->flags;
+                        existing->cinfo = address->cinfo;
 
-        switch (type) {
-        case RTM_NEWADDR:
-                if (!address_dropped)
-                        log_link_debug(link, "added address: %s/%u (valid for %s)",
-                                       buf, address->prefixlen, valid_str);
-                else
-                        log_link_debug(link, "updated address: %s/%u (valid for %s)",
-                                       buf, address->prefixlen, valid_str);
+                } else {
+                        log_link_debug(link, "Adding address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
 
-                LIST_PREPEND(addresses, link->addresses, address);
-                address = NULL;
+                        LIST_PREPEND(addresses, link->addresses, address);
+                        address_establish(address, link);
 
-                link_save(link);
+                        address = NULL;
+
+                        link_save(link);
+                }
 
                 break;
+
         case RTM_DELADDR:
-                if (address_dropped) {
-                        log_link_debug(link, "removed address: %s/%u (valid for %s)",
-                                       buf, address->prefixlen, valid_str);
 
-                        link_save(link);
+                if (existing) {
+                        log_link_debug(link, "Removing address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
+                        address_release(existing, link);
+                        LIST_REMOVE(addresses, link->addresses, existing);
+                        address_free(existing);
                 } else
-                        log_link_warning(link,
-                                         "removing non-existent address: %s/%u (valid for %s)",
-                                         buf, address->prefixlen, valid_str);
+                        log_link_warning(link, "Removing non-existent address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str);
 
                 break;
         default: