]> 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 4137b2994527c570f67d5139538082bce32e4499..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(&address->link->manager->fw_ctx, 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) {
@@ -474,8 +482,8 @@ static void log_address_debug(const Address *address, const char *str, const Lin
 
         if (DEBUG_LOGGING) {
                 _cleanup_free_ char *addr = NULL, *peer = NULL;
-                char valid_buf[FORMAT_TIMESPAN_MAX];
-                const char *valid_str = 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);
@@ -488,10 +496,16 @@ static void log_address_debug(const Address *address, const char *str, const Lin
                                                     address->cinfo.ifa_valid * USEC_PER_SEC,
                                                     USEC_PER_SEC);
 
-                log_link_debug(link, "%s address: %s%s%s/%u (valid %s%s)",
+                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));
+                               valid_str ? "for " : "forever", strempty(valid_str),
+                               preferred_str ? "for " : "forever", strempty(preferred_str));
         }
 }
 
@@ -514,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,
@@ -536,13 +581,9 @@ int address_remove(
         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);
+        r = address_set_netlink_message(address, req, link);
         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);
-        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,
@@ -599,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);
@@ -614,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;
 
@@ -803,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));
@@ -831,6 +870,8 @@ int address_configure(
         if (acquired_address)
                 address = acquired_address;
 
+        update = address_get(link, address, NULL) >= 0;
+
         log_address_debug(address, update ? "Updating" : "Configuring", link);
 
         if (update)
@@ -842,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);
-        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);
+        r = address_set_netlink_message(address, req, link);
         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)
@@ -885,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)
@@ -910,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);
@@ -940,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);
 }
 
@@ -987,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");
 
@@ -1029,10 +1060,7 @@ int link_set_addresses(Link *link) {
         }
 
         ORDERED_HASHMAP_FOREACH(ad, link->network->addresses_by_section) {
-                bool update;
-
-                update = address_get(link, ad, NULL) > 0;
-                r = static_address_configure(ad, link, update);
+                r = static_address_configure(ad, link);
                 if (r < 0)
                         return r;
         }
@@ -1056,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;
         }
@@ -1064,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;
@@ -1455,7 +1488,7 @@ int config_parse_broadcast(
         }
 
         n->family = AF_INET;
-        n = NULL;
+        TAKE_PTR(n);
 
         return 0;
 }
@@ -1538,8 +1571,7 @@ int config_parse_address(
         else
                 n->in_addr_peer = buffer;
 
-        n = NULL;
-
+        TAKE_PTR(n);
         return 0;
 }
 
@@ -1584,7 +1616,7 @@ int config_parse_label(
         if (r < 0)
                 return log_oom();
 
-        n = NULL;
+        TAKE_PTR(n);
         return 0;
 }
 
@@ -1680,7 +1712,7 @@ int config_parse_address_flags(
 
         SET_FLAG(n->flags, ltype, r);
 
-        n = NULL;
+        TAKE_PTR(n);
         return 0;
 }
 
@@ -1731,7 +1763,7 @@ int config_parse_address_scope(
         }
 
         n->scope_set = true;
-        n = NULL;
+        TAKE_PTR(n);
         return 0;
 }
 
@@ -1749,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);
@@ -1778,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;
 }