]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-address.c
Merge pull request #18007 from fw-strlen/ipv6_masq_and_dnat
[thirdparty/systemd.git] / src / network / networkd-address.c
index 59357a6b1c975ec732741f4f89eeb4dd54844e15..d862e45e61c732a4dbd2d1b109f98bbee6f351c3 100644 (file)
@@ -9,6 +9,7 @@
 #include "netlink-util.h"
 #include "networkd-address-pool.h"
 #include "networkd-address.h"
+#include "networkd-ipv6-proxy-ndp.h"
 #include "networkd-manager.h"
 #include "networkd-network.h"
 #include "parse-util.h"
@@ -94,11 +95,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s
         address->network = network;
         address->section = TAKE_PTR(n);
 
-        r = ordered_hashmap_ensure_allocated(&network->addresses_by_section, &network_config_hash_ops);
-        if (r < 0)
-                return r;
-
-        r = ordered_hashmap_put(network->addresses_by_section, address->section, address);
+        r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address);
         if (r < 0)
                 return r;
 
@@ -265,16 +262,23 @@ static int address_set_masquerade(Address *address, bool add) {
         if (!address->link->network)
                 return 0;
 
-        if (!address->link->network->ip_masquerade)
+        if (address->family == AF_INET &&
+            !FLAGS_SET(address->link->network->ip_masquerade, ADDRESS_FAMILY_IPV4))
                 return 0;
 
-        if (address->family != AF_INET)
+        if (address->family == AF_INET6 &&
+            !FLAGS_SET(address->link->network->ip_masquerade, ADDRESS_FAMILY_IPV6))
                 return 0;
 
         if (address->scope >= RT_SCOPE_LINK)
                 return 0;
 
-        if (address->ip_masquerade_done == add)
+        if (address->family == AF_INET &&
+            address->ip_masquerade_done == add)
+                return 0;
+
+        if (address->family == AF_INET6 &&
+            address->ipv6_masquerade_done == add)
                 return 0;
 
         masked = address->in_addr;
@@ -282,11 +286,14 @@ static int address_set_masquerade(Address *address, bool add) {
         if (r < 0)
                 return r;
 
-        r = fw_add_masquerade(add, AF_INET, &masked, address->prefixlen);
+        r = fw_add_masquerade(&address->link->manager->fw_ctx, add, address->family, &masked, address->prefixlen);
         if (r < 0)
                 return r;
 
-        address->ip_masquerade_done = add;
+        if (address->family == AF_INET)
+                address->ip_masquerade_done = add;
+        else if (address->family == AF_INET6)
+                address->ipv6_masquerade_done = add;
 
         return 0;
 }
@@ -329,6 +336,7 @@ static int address_add_foreign(Link *link, const Address *in, Address **ret) {
 }
 
 static int address_add(Link *link, const Address *in, Address **ret) {
+        bool is_new = false;
         Address *address;
         int r;
 
@@ -341,6 +349,7 @@ static int address_add(Link *link, const Address *in, Address **ret) {
                 r = address_add_internal(link, &link->addresses, in, &address);
                 if (r < 0)
                         return r;
+                is_new = true;
         } else if (r == 0) {
                 /* Take over a foreign address */
                 r = set_ensure_put(&link->addresses, &address_hash_ops, address);
@@ -356,8 +365,7 @@ static int address_add(Link *link, const Address *in, Address **ret) {
 
         if (ret)
                 *ret = address;
-
-        return 0;
+        return is_new;
 }
 
 static int address_update(Address *address, const Address *src) {
@@ -405,7 +413,8 @@ static int address_drop(Address *address) {
         bool ready;
         int r;
 
-        assert(address);
+        if (!address)
+                return 0;
 
         ready = address_is_ready(address);
         link = address->link;
@@ -466,6 +475,40 @@ int link_has_ipv6_address(Link *link, const struct in6_addr *address) {
         return address_get(link, a, NULL) >= 0;
 }
 
+static void log_address_debug(const Address *address, const char *str, const Link *link) {
+        assert(address);
+        assert(str);
+        assert(link);
+
+        if (DEBUG_LOGGING) {
+                _cleanup_free_ char *addr = NULL, *peer = NULL;
+                char valid_buf[FORMAT_TIMESPAN_MAX], preferred_buf[FORMAT_TIMESPAN_MAX];
+                const char *valid_str = NULL, *preferred_str = NULL;
+                bool has_peer;
+
+                (void) in_addr_to_string(address->family, &address->in_addr, &addr);
+                has_peer = in_addr_is_null(address->family, &address->in_addr_peer) == 0;
+                if (has_peer)
+                        (void) in_addr_to_string(address->family, &address->in_addr_peer, &peer);
+
+                if (address->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
+                        valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
+                                                    address->cinfo.ifa_valid * USEC_PER_SEC,
+                                                    USEC_PER_SEC);
+
+                if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME)
+                        preferred_str = format_timespan(preferred_buf, FORMAT_TIMESPAN_MAX,
+                                                        address->cinfo.ifa_prefered * USEC_PER_SEC,
+                                                        USEC_PER_SEC);
+
+                log_link_debug(link, "%s address: %s%s%s/%u (valid %s%s, preferred %s%s)",
+                               str, strnull(addr), has_peer ? " peer " : "",
+                               has_peer ? strnull(peer) : "", address->prefixlen,
+                               valid_str ? "for " : "forever", strempty(valid_str),
+                               preferred_str ? "for " : "forever", strempty(preferred_str));
+        }
+}
+
 static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
         int r;
 
@@ -485,6 +528,37 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link
         return 1;
 }
 
+static int address_set_netlink_message(const Address *address, sd_netlink_message *req, Link *link) {
+        int r;
+
+        assert(address);
+        assert(req);
+        assert(link);
+
+        r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set prefixlen: %m");
+
+        /* On remove, only IFA_F_MANAGETEMPADDR flag for IPv6 addresses are used. But anyway, set all
+         * flags here unconditionally. Without setting the flag, the template addresses generated by
+         * kernel will not be removed automatically when the main address is removed. */
+        r = sd_rtnl_message_addr_set_flags(req, address->flags & 0xff);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not set flags: %m");
+
+        if ((address->flags & ~0xff) != 0) {
+                r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags);
+                if (r < 0)
+                        return log_link_error_errno(link, r, "Could not set extended flags: %m");
+        }
+
+        r = netlink_message_append_in_addr_union(req, IFA_LOCAL, address->family, &address->in_addr);
+        if (r < 0)
+                return log_link_error_errno(link, r, "Could not append IFA_LOCAL attribute: %m");
+
+        return 0;
+}
+
 int address_remove(
                 const Address *address,
                 Link *link,
@@ -500,25 +574,16 @@ int address_remove(
         assert(link->manager);
         assert(link->manager->rtnl);
 
-        if (DEBUG_LOGGING) {
-                _cleanup_free_ char *b = NULL;
-
-                (void) in_addr_to_string(address->family, &address->in_addr, &b);
-                log_link_debug(link, "Removing address %s", strna(b));
-        }
+        log_address_debug(address, "Removing", link);
 
         r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR,
                                      link->ifindex, address->family);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not allocate RTM_DELADDR message: %m");
 
-        r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not set prefixlen: %m");
-
-        r = netlink_message_append_in_addr_union(req, IFA_LOCAL, address->family, &address->in_addr);
+        r = address_set_netlink_message(address, req, link);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not append IFA_LOCAL attribute: %m");
+                return r;
 
         r = netlink_call_async(link->manager->rtnl, NULL, req,
                                callback ?: address_remove_handler,
@@ -575,7 +640,6 @@ bool link_address_is_dynamic(const Link *link, const Address *address) {
 
 static int link_enumerate_ipv6_tentative_addresses(Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
-        sd_netlink_message *addr;
         int r;
 
         assert(link);
@@ -590,7 +654,7 @@ static int link_enumerate_ipv6_tentative_addresses(Link *link) {
         if (r < 0)
                 return r;
 
-        for (addr = reply; addr; addr = sd_netlink_message_next(addr)) {
+        for (sd_netlink_message *addr = reply; addr; addr = sd_netlink_message_next(addr)) {
                 unsigned char flags;
                 int ifindex;
 
@@ -779,13 +843,12 @@ int address_configure(
                 const Address *address,
                 Link *link,
                 link_netlink_message_handler_t callback,
-                bool update,
                 Address **ret) {
 
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
         Address *acquired_address, *a;
-        uint32_t flags;
-        int r;
+        bool update;
+        int r, k;
 
         assert(address);
         assert(IN_SET(address->family, AF_INET, AF_INET6));
@@ -807,12 +870,9 @@ int address_configure(
         if (acquired_address)
                 address = acquired_address;
 
-        if (DEBUG_LOGGING) {
-                _cleanup_free_ char *str = NULL;
+        update = address_get(link, address, NULL) >= 0;
 
-                (void) in_addr_to_string(address->family, &address->in_addr, &str);
-                log_link_debug(link, "%s address: %s", update ? "Updating" : "Configuring", strna(str));
-        }
+        log_address_debug(address, update ? "Updating" : "Configuring", link);
 
         if (update)
                 r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req,
@@ -823,29 +883,14 @@ int address_configure(
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not allocate RTM_NEWADDR message: %m");
 
-        r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen);
+        r = address_set_netlink_message(address, req, link);
         if (r < 0)
-                return log_link_error_errno(link, r, "Could not set prefixlen: %m");
-
-        flags = address->flags | IFA_F_PERMANENT;
-        r = sd_rtnl_message_addr_set_flags(req, flags & 0xff);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not set flags: %m");
-
-        if (flags & ~0xff) {
-                r = sd_netlink_message_append_u32(req, IFA_FLAGS, flags);
-                if (r < 0)
-                        return log_link_error_errno(link, r, "Could not set extended flags: %m");
-        }
+                return r;
 
         r = sd_rtnl_message_addr_set_scope(req, address->scope);
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not set scope: %m");
 
-        r = netlink_message_append_in_addr_union(req, IFA_LOCAL, address->family, &address->in_addr);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not append IFA_LOCAL attribute: %m");
-
         if (in_addr_is_null(address->family, &address->in_addr_peer) == 0) {
                 r = netlink_message_append_in_addr_union(req, IFA_ADDRESS, address->family, &address->in_addr_peer);
                 if (r < 0)
@@ -866,9 +911,9 @@ int address_configure(
         if (r < 0)
                 return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m");
 
-        r = address_add(link, address, &a);
-        if (r < 0)
-                return log_link_error_errno(link, r, "Could not add address: %m");
+        k = address_add(link, address, &a);
+        if (k < 0)
+                return log_link_error_errno(link, k, "Could not add address: %m");
 
         r = address_set_masquerade(a, true);
         if (r < 0)
@@ -891,12 +936,13 @@ int address_configure(
         if (ret)
                 *ret = a;
 
-        return 1;
+        return k;
 }
 
 static int static_address_ready_callback(Address *address) {
         Address *a;
         Link *link;
+        int r;
 
         assert(address);
         assert(address->link);
@@ -921,6 +967,10 @@ static int static_address_ready_callback(Address *address) {
 
         link->addresses_ready = true;
 
+        r = link_set_ipv6_proxy_ndp_addresses(link);
+        if (r < 0)
+                return r;
+
         return link_set_routes(link);
 }
 
@@ -956,16 +1006,11 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
                  * will not be called automatically. So, call it here. */
                 a = set_first(link->static_addresses);
                 if (!a) {
-                        log_link_warning(link, "No static address is stored.");
-                        link_enter_failed(link);
+                        log_link_debug(link, "No static address is stored. Already removed?");
                         return 1;
                 }
-                if (!a->callback) {
-                        log_link_warning(link, "Address ready callback is not set.");
-                        link_enter_failed(link);
-                        return 1;
-                }
-                r = a->callback(a);
+
+                r = static_address_ready_callback(a);
                 if (r < 0)
                         link_enter_failed(link);
         }
@@ -973,14 +1018,14 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link)
         return 1;
 }
 
-static int static_address_configure(const Address *address, Link *link, bool update) {
+static int static_address_configure(const Address *address, Link *link) {
         Address *ret;
         int r;
 
         assert(address);
         assert(link);
 
-        r = address_configure(address, link, address_handler, update, &ret);
+        r = address_configure(address, link, address_handler, &ret);
         if (r < 0)
                 return log_link_warning_errno(link, r, "Could not configure static address: %m");
 
@@ -1009,11 +1054,13 @@ int link_set_addresses(Link *link) {
                 return 0;
         }
 
-        ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) {
-                bool update;
+        if (link->address_messages != 0) {
+                log_link_debug(link, "Static addresses are configuring.");
+                return 0;
+        }
 
-                update = address_get(link, ad, NULL) > 0;
-                r = static_address_configure(ad, link, update);
+        ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) {
+                r = static_address_configure(ad, link);
                 if (r < 0)
                         return r;
         }
@@ -1037,7 +1084,7 @@ int link_set_addresses(Link *link) {
                         return log_link_warning_errno(link, r, "Could not generate EUI64 address: %m");
 
                 address->family = AF_INET6;
-                r = static_address_configure(address, link, true);
+                r = static_address_configure(address, link);
                 if (r < 0)
                         return r;
         }
@@ -1045,6 +1092,11 @@ int link_set_addresses(Link *link) {
         if (link->address_messages == 0) {
                 link->addresses_configured = true;
                 link->addresses_ready = true;
+
+                r = link_set_ipv6_proxy_ndp_addresses(link);
+                if (r < 0)
+                        return r;
+
                 r = link_set_routes(link);
                 if (r < 0)
                         return r;
@@ -1058,15 +1110,11 @@ int link_set_addresses(Link *link) {
 
 int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) {
         _cleanup_(address_freep) Address *tmp = NULL;
-        _cleanup_free_ char *buf = NULL, *buf_peer = NULL;
         Link *link = NULL;
         uint16_t type;
         unsigned char flags;
         Address *address = NULL;
-        char valid_buf[FORMAT_TIMESPAN_MAX];
-        const char *valid_str = NULL;
         int ifindex, r;
-        bool has_peer = false;
 
         assert(rtnl);
         assert(message);
@@ -1154,8 +1202,6 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 } else if (r >= 0) {
                         if (in4_addr_equal(&tmp->in_addr.in, &tmp->in_addr_peer.in))
                                 tmp->in_addr_peer = IN_ADDR_NULL;
-                        else
-                                has_peer = true;
                 }
 
                 r = sd_netlink_message_read_in_addr(message, IFA_BROADCAST, &tmp->broadcast);
@@ -1182,7 +1228,6 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                                 log_link_warning_errno(link, r, "rtnl: could not get peer address from address message, ignoring: %m");
                                 return 0;
                         }
-                        has_peer = true;
                 } else if (r == -ENODATA) {
                         /* Does not have peer address. */
                         r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &tmp->in_addr.in6);
@@ -1201,39 +1246,28 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 assert_not_reached("Received unsupported address family");
         }
 
-        (void) in_addr_to_string(tmp->family, &tmp->in_addr, &buf);
-        (void) in_addr_to_string(tmp->family, &tmp->in_addr_peer, &buf_peer);
-
         r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &tmp->cinfo);
         if (r < 0 && r != -ENODATA) {
                 log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m");
                 return 0;
-        } else if (r >= 0 && tmp->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME)
-                valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX,
-                                            tmp->cinfo.ifa_valid * USEC_PER_SEC,
-                                            USEC_PER_SEC);
+        }
 
         (void) address_get(link, tmp, &address);
 
         switch (type) {
         case RTM_NEWADDR:
-                if (address)
-                        log_link_debug(link, "Remembering updated address: %s%s%s/%u (valid %s%s)",
-                                       strnull(buf), has_peer ? " peer " : "",
-                                       has_peer ? strnull(buf_peer) : "", tmp->prefixlen,
-                                       valid_str ? "for " : "forever", strempty(valid_str));
-                else {
+                log_address_debug(tmp, address ? "Remembering updated" : "Remembering foreign", link);
+                if (!address) {
                         /* An address appeared that we did not request */
                         r = address_add_foreign(link, tmp, &address);
                         if (r < 0) {
+                                _cleanup_free_ char *buf = NULL;
+
+                                (void) in_addr_to_string(tmp->family, &tmp->in_addr, &buf);
                                 log_link_warning_errno(link, r, "Failed to remember foreign address %s/%u, ignoring: %m",
                                                        strnull(buf), tmp->prefixlen);
                                 return 0;
-                        } else
-                                log_link_debug(link, "Remembering foreign address: %s%s%s/%u (valid %s%s)",
-                                               strnull(buf), has_peer ? " peer " : "",
-                                               has_peer ? strnull(buf_peer) : "", tmp->prefixlen,
-                                               valid_str ? "for " : "forever", strempty(valid_str));
+                        }
                 }
 
                 /* address_update() logs internally, so we don't need to here. */
@@ -1244,17 +1278,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 break;
 
         case RTM_DELADDR:
-                if (address) {
-                        log_link_debug(link, "Forgetting address: %s%s%s/%u (valid %s%s)",
-                                       strnull(buf), has_peer ? " peer " : "",
-                                       has_peer ? strnull(buf_peer) : "", tmp->prefixlen,
-                                       valid_str ? "for " : "forever", strempty(valid_str));
-                        (void) address_drop(address);
-                } else
-                        log_link_debug(link, "Kernel removed an address we don't remember: %s%s%s/%u (valid %s%s), ignoring.",
-                                       strnull(buf), has_peer ? " peer " : "",
-                                       has_peer ? strnull(buf_peer) : "", tmp->prefixlen,
-                                       valid_str ? "for " : "forever", strempty(valid_str));
+                log_address_debug(tmp, address ? "Forgetting" : "Kernel removed unknown", link);
+                (void) address_drop(address);
 
                 break;
 
@@ -1463,7 +1488,7 @@ int config_parse_broadcast(
         }
 
         n->family = AF_INET;
-        n = NULL;
+        TAKE_PTR(n);
 
         return 0;
 }
@@ -1546,8 +1571,7 @@ int config_parse_address(
         else
                 n->in_addr_peer = buffer;
 
-        n = NULL;
-
+        TAKE_PTR(n);
         return 0;
 }
 
@@ -1592,7 +1616,7 @@ int config_parse_label(
         if (r < 0)
                 return log_oom();
 
-        n = NULL;
+        TAKE_PTR(n);
         return 0;
 }
 
@@ -1688,7 +1712,7 @@ int config_parse_address_flags(
 
         SET_FLAG(n->flags, ltype, r);
 
-        n = NULL;
+        TAKE_PTR(n);
         return 0;
 }
 
@@ -1739,7 +1763,7 @@ int config_parse_address_scope(
         }
 
         n->scope_set = true;
-        n = NULL;
+        TAKE_PTR(n);
         return 0;
 }
 
@@ -1757,7 +1781,6 @@ int config_parse_duplicate_address_detection(
 
         Network *network = userdata;
         _cleanup_(address_free_or_set_invalidp) Address *n = NULL;
-        AddressFamily a;
         int r;
 
         assert(filename);
@@ -1786,15 +1809,15 @@ int config_parse_duplicate_address_detection(
                 return 0;
         }
 
-        a = duplicate_address_detection_address_family_from_string(rvalue);
+        AddressFamily a = duplicate_address_detection_address_family_from_string(rvalue);
         if (a < 0) {
-                log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
+                log_syntax(unit, LOG_WARNING, filename, line, a,
                            "Failed to parse %s=, ignoring: %s", lvalue, rvalue);
                 return 0;
         }
-
         n->duplicate_address_detection = a;
-        n = NULL;
+
+        TAKE_PTR(n);
         return 0;
 }