LCOV - code coverage report
Current view: top level - network - networkd-dhcp6.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 449 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 23 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 533 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : /***
       3                 :            :   Copyright © 2014 Intel Corporation. All rights reserved.
       4                 :            : ***/
       5                 :            : 
       6                 :            : #include <netinet/in.h>
       7                 :            : #include <linux/if.h>
       8                 :            : #include <linux/if_arp.h>
       9                 :            : #include "sd-radv.h"
      10                 :            : 
      11                 :            : #include "sd-dhcp6-client.h"
      12                 :            : 
      13                 :            : #include "hashmap.h"
      14                 :            : #include "hostname-util.h"
      15                 :            : #include "missing_network.h"
      16                 :            : #include "network-internal.h"
      17                 :            : #include "networkd-dhcp6.h"
      18                 :            : #include "networkd-link.h"
      19                 :            : #include "networkd-manager.h"
      20                 :            : #include "siphash24.h"
      21                 :            : #include "string-util.h"
      22                 :            : #include "radv-internal.h"
      23                 :            : 
      24                 :            : static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
      25                 :            : static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
      26                 :            : static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
      27                 :            : static int dhcp6_prefix_remove_all(Manager *m, Link *link);
      28                 :            : 
      29                 :          0 : static bool dhcp6_get_prefix_delegation(Link *link) {
      30         [ #  # ]:          0 :         if (!link->network)
      31                 :          0 :                 return false;
      32                 :            : 
      33         [ #  # ]:          0 :         return IN_SET(link->network->router_prefix_delegation,
      34                 :            :                       RADV_PREFIX_DELEGATION_DHCP6,
      35                 :            :                       RADV_PREFIX_DELEGATION_BOTH);
      36                 :            : }
      37                 :            : 
      38                 :          0 : static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) {
      39                 :            :         Manager *manager;
      40                 :            :         Link *l;
      41                 :            :         Iterator i;
      42                 :            : 
      43         [ #  # ]:          0 :         assert(dhcp6_link);
      44                 :            : 
      45                 :          0 :         manager = dhcp6_link->manager;
      46         [ #  # ]:          0 :         assert(manager);
      47                 :            : 
      48         [ #  # ]:          0 :         HASHMAP_FOREACH(l, manager->links, i) {
      49         [ #  # ]:          0 :                 if (l == dhcp6_link)
      50                 :          0 :                         continue;
      51                 :            : 
      52         [ #  # ]:          0 :                 if (!dhcp6_get_prefix_delegation(l))
      53                 :          0 :                         continue;
      54                 :            : 
      55                 :          0 :                 return true;
      56                 :            :         }
      57                 :            : 
      58                 :          0 :         return false;
      59                 :            : }
      60                 :            : 
      61                 :          0 : static int dhcp6_lease_information_acquired(sd_dhcp6_client *client,
      62                 :            :                                         Link *link) {
      63                 :          0 :         return 0;
      64                 :            : }
      65                 :            : 
      66                 :          0 : static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
      67                 :            :                                   uint8_t prefix_len,
      68                 :            :                                   uint32_t lifetime_preferred,
      69                 :            :                                   uint32_t lifetime_valid) {
      70                 :          0 :         sd_radv *radv = link->radv;
      71                 :            :         int r;
      72                 :          0 :         _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
      73                 :            : 
      74                 :          0 :         r = sd_radv_prefix_new(&p);
      75         [ #  # ]:          0 :         if (r < 0)
      76                 :          0 :                 return r;
      77                 :            : 
      78                 :          0 :         r = sd_radv_prefix_set_prefix(p, prefix, prefix_len);
      79         [ #  # ]:          0 :         if (r < 0)
      80                 :          0 :                 return r;
      81                 :            : 
      82                 :          0 :         r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred);
      83         [ #  # ]:          0 :         if (r < 0)
      84                 :          0 :                 return r;
      85                 :            : 
      86                 :          0 :         r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid);
      87         [ #  # ]:          0 :         if (r < 0)
      88                 :          0 :                 return r;
      89                 :            : 
      90                 :          0 :         r = sd_radv_stop(radv);
      91         [ #  # ]:          0 :         if (r < 0)
      92                 :          0 :                 return r;
      93                 :            : 
      94                 :          0 :         r = sd_radv_add_prefix(radv, p, true);
      95   [ #  #  #  # ]:          0 :         if (r < 0 && r != -EEXIST)
      96                 :          0 :                 return r;
      97                 :            : 
      98                 :          0 :         r = dhcp6_prefix_add(link->manager, prefix, link);
      99         [ #  # ]:          0 :         if (r < 0)
     100                 :          0 :                 return r;
     101                 :            : 
     102                 :          0 :         return sd_radv_start(radv);
     103                 :            : }
     104                 :            : 
     105                 :          0 : static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
     106                 :            :         int r;
     107                 :            : 
     108         [ #  # ]:          0 :         assert(link);
     109                 :            : 
     110   [ #  #  #  # ]:          0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     111                 :          0 :                 return 1;
     112                 :            : 
     113                 :          0 :         r = sd_netlink_message_get_errno(m);
     114         [ #  # ]:          0 :         if (r < 0)
     115   [ #  #  #  # ]:          0 :                 log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnet: %m");
     116                 :            : 
     117                 :          0 :         return 1;
     118                 :            : }
     119                 :            : 
     120                 :          0 : int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) {
     121                 :            :         int r;
     122                 :            :         sd_dhcp6_lease *lease;
     123                 :            :         union in_addr_union pd_prefix;
     124                 :            :         uint8_t pd_prefix_len;
     125                 :            :         uint32_t lifetime_preferred, lifetime_valid;
     126                 :            : 
     127                 :          0 :         r = sd_dhcp6_client_get_lease(client, &lease);
     128         [ #  # ]:          0 :         if (r < 0)
     129                 :          0 :                 return r;
     130                 :            : 
     131                 :          0 :         sd_dhcp6_lease_reset_pd_prefix_iter(lease);
     132                 :            : 
     133         [ #  # ]:          0 :         while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
     134                 :            :                                      &lifetime_preferred,
     135                 :            :                                      &lifetime_valid) >= 0) {
     136         [ #  # ]:          0 :                 _cleanup_free_ char *buf = NULL;
     137                 :            :                 Route *route;
     138                 :            : 
     139         [ #  # ]:          0 :                 if (pd_prefix_len >= 64)
     140                 :          0 :                         continue;
     141                 :            : 
     142                 :          0 :                 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
     143                 :            : 
     144                 :          0 :                 r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, NULL, 0, 0, 0, &route);
     145         [ #  # ]:          0 :                 if (r < 0) {
     146   [ #  #  #  # ]:          0 :                         log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m",
     147                 :            :                                                strnull(buf),
     148                 :            :                                                pd_prefix_len);
     149                 :          0 :                         continue;
     150                 :            :                 }
     151                 :            : 
     152                 :          0 :                 route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
     153                 :            : 
     154                 :          0 :                 r = route_remove(route, link, dhcp6_route_remove_handler);
     155         [ #  # ]:          0 :                 if (r < 0) {
     156   [ #  #  #  # ]:          0 :                         log_link_warning_errno(link, r, "Cannot delete unreachable route for DHCPv6 delegated subnet %s/%u: %m",
     157                 :            :                                                strnull(buf),
     158                 :            :                                                pd_prefix_len);
     159                 :          0 :                         continue;
     160                 :            :                 }
     161                 :            : 
     162   [ #  #  #  # ]:          0 :                 log_link_debug(link, "Removing unreachable route %s/%u",
     163                 :            :                                strnull(buf), pd_prefix_len);
     164                 :            :         }
     165                 :            : 
     166                 :          0 :         return 0;
     167                 :            : }
     168                 :            : 
     169                 :          0 : static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
     170                 :            :                                       struct in6_addr *pd_prefix,
     171                 :            :                                       uint8_t pd_prefix_len,
     172                 :            :                                       uint32_t lifetime_preferred,
     173                 :            :                                       uint32_t lifetime_valid) {
     174                 :            :         Link *link;
     175                 :          0 :         Manager *manager = dhcp6_link->manager;
     176                 :            :         union in_addr_union prefix;
     177                 :          0 :         uint64_t n_prefixes, n_used = 0;
     178                 :          0 :         _cleanup_free_ char *buf = NULL;
     179                 :          0 :         _cleanup_free_ char *assigned_buf = NULL;
     180                 :            :         int r;
     181                 :            : 
     182         [ #  # ]:          0 :         assert(manager);
     183         [ #  # ]:          0 :         assert(pd_prefix_len <= 64);
     184                 :            : 
     185                 :          0 :         prefix.in6 = *pd_prefix;
     186                 :            : 
     187                 :          0 :         r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len);
     188         [ #  # ]:          0 :         if (r < 0)
     189                 :          0 :                 return r;
     190                 :            : 
     191                 :          0 :         n_prefixes = UINT64_C(1) << (64 - pd_prefix_len);
     192                 :            : 
     193                 :          0 :         (void) in_addr_to_string(AF_INET6, &prefix, &buf);
     194   [ #  #  #  # ]:          0 :         log_link_debug(dhcp6_link, "Assigning up to %" PRIu64 " prefixes from %s/%u",
     195                 :            :                        n_prefixes, strnull(buf), pd_prefix_len);
     196                 :            : 
     197         [ #  # ]:          0 :         while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) {
     198                 :            :                 Link *assigned_link;
     199                 :            : 
     200         [ #  # ]:          0 :                 if (n_used == n_prefixes) {
     201   [ #  #  #  # ]:          0 :                         log_link_debug(dhcp6_link, "Assigned %" PRIu64 "/%" PRIu64 " prefixes from %s/%u",
     202                 :            :                                        n_used, n_prefixes, strnull(buf), pd_prefix_len);
     203                 :            : 
     204                 :          0 :                         return -EAGAIN;
     205                 :            :                 }
     206                 :            : 
     207         [ #  # ]:          0 :                 if (link == dhcp6_link)
     208                 :          0 :                         continue;
     209                 :            : 
     210         [ #  # ]:          0 :                 if (!dhcp6_get_prefix_delegation(link))
     211                 :          0 :                         continue;
     212                 :            : 
     213                 :          0 :                 assigned_link = dhcp6_prefix_get(manager, &prefix.in6);
     214   [ #  #  #  # ]:          0 :                 if (assigned_link && assigned_link != link)
     215                 :          0 :                         continue;
     216                 :            : 
     217                 :          0 :                 (void) in_addr_to_string(AF_INET6, &prefix, &assigned_buf);
     218                 :          0 :                 r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64,
     219                 :            :                                            lifetime_preferred, lifetime_valid);
     220         [ #  # ]:          0 :                 if (r < 0) {
     221   [ #  #  #  #  :          0 :                         log_link_error_errno(link, r, "Unable to %s prefix %s/64 from %s/%u for link: %m",
             #  #  #  # ]
     222                 :            :                                              assigned_link ? "update": "assign",
     223                 :            :                                              strnull(assigned_buf),
     224                 :            :                                              strnull(buf), pd_prefix_len);
     225                 :            : 
     226         [ #  # ]:          0 :                         if (!assigned_link)
     227                 :          0 :                                 continue;
     228                 :            : 
     229                 :            :                 } else
     230   [ #  #  #  # ]:          0 :                         log_link_debug(link, "Assigned prefix %" PRIu64 "/%" PRIu64 " %s/64 from %s/%u to link",
     231                 :            :                                        n_used + 1, n_prefixes,
     232                 :            :                                        strnull(assigned_buf),
     233                 :            :                                        strnull(buf), pd_prefix_len);
     234                 :            : 
     235                 :          0 :                 n_used++;
     236                 :            : 
     237                 :          0 :                 r = in_addr_prefix_next(AF_INET6, &prefix, 64);
     238   [ #  #  #  # ]:          0 :                 if (r < 0 && n_used < n_prefixes)
     239                 :          0 :                         return r;
     240                 :            :         }
     241                 :            : 
     242                 :          0 :         return 0;
     243                 :            : }
     244                 :            : 
     245                 :          0 : static int dhcp6_route_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
     246                 :            :         int r;
     247                 :            : 
     248         [ #  # ]:          0 :         assert(link);
     249                 :            : 
     250   [ #  #  #  # ]:          0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     251                 :          0 :                 return 1;
     252                 :            : 
     253                 :          0 :         r = sd_netlink_message_get_errno(m);
     254   [ #  #  #  # ]:          0 :         if (r < 0 && r != -EEXIST)
     255   [ #  #  #  # ]:          0 :                 log_link_debug_errno(link, r, "Received error when adding unreachable route for DHCPv6 delegated subnet: %m");
     256                 :            : 
     257                 :          0 :         return 1;
     258                 :            : }
     259                 :            : 
     260                 :          0 : static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) {
     261                 :            :         int r;
     262                 :            :         sd_dhcp6_lease *lease;
     263                 :            :         union in_addr_union pd_prefix;
     264                 :            :         uint8_t pd_prefix_len;
     265                 :            :         uint32_t lifetime_preferred, lifetime_valid;
     266                 :          0 :         Iterator i = ITERATOR_FIRST;
     267                 :            : 
     268                 :          0 :         r = sd_dhcp6_client_get_lease(client, &lease);
     269         [ #  # ]:          0 :         if (r < 0)
     270                 :          0 :                 return r;
     271                 :            : 
     272                 :          0 :         sd_dhcp6_lease_reset_pd_prefix_iter(lease);
     273                 :            : 
     274         [ #  # ]:          0 :         while (sd_dhcp6_lease_get_pd(lease, &pd_prefix.in6, &pd_prefix_len,
     275                 :            :                                      &lifetime_preferred,
     276                 :            :                                      &lifetime_valid) >= 0) {
     277                 :            : 
     278      [ #  #  # ]:          0 :                 _cleanup_free_ char *buf = NULL;
     279                 :            : 
     280                 :          0 :                 (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf);
     281                 :            : 
     282         [ #  # ]:          0 :                 if (pd_prefix_len > 64) {
     283   [ #  #  #  # ]:          0 :                         log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u",
     284                 :            :                                        strnull(buf), pd_prefix_len);
     285                 :          0 :                         continue;
     286                 :            :                 }
     287                 :            : 
     288         [ #  # ]:          0 :                 if (pd_prefix_len < 48)
     289   [ #  #  #  # ]:          0 :                         log_link_warning(link, "PD Prefix length < 48, looks unusual %s/%u",
     290                 :            :                                        strnull(buf), pd_prefix_len);
     291                 :            : 
     292         [ #  # ]:          0 :                 if (pd_prefix_len < 64) {
     293                 :            :                         uint32_t table;
     294                 :            :                         Route *route;
     295                 :            : 
     296                 :          0 :                         table = link_get_dhcp_route_table(link);
     297                 :            : 
     298                 :          0 :                         r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, NULL, 0, 0, table, &route);
     299         [ #  # ]:          0 :                         if (r < 0) {
     300   [ #  #  #  # ]:          0 :                                 log_link_warning_errno(link, r, "Failed to add unreachable route for DHCPv6 delegated subnet %s/%u: %m",
     301                 :            :                                                        strnull(buf),
     302                 :            :                                                        pd_prefix_len);
     303                 :          0 :                                 continue;
     304                 :            :                         }
     305                 :            : 
     306                 :          0 :                         route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE);
     307                 :            : 
     308                 :          0 :                         r = route_configure(route, link, dhcp6_route_handler);
     309         [ #  # ]:          0 :                         if (r < 0) {
     310   [ #  #  #  # ]:          0 :                                 log_link_warning_errno(link, r, "Cannot configure unreachable route for delegated subnet %s/%u: %m",
     311                 :            :                                                        strnull(buf),
     312                 :            :                                                        pd_prefix_len);
     313                 :          0 :                                 continue;
     314                 :            :                         }
     315                 :            : 
     316   [ #  #  #  # ]:          0 :                         log_link_debug(link, "Configuring unreachable route for %s/%u",
     317                 :            :                                        strnull(buf), pd_prefix_len);
     318                 :            :                 } else
     319   [ #  #  #  # ]:          0 :                         log_link_debug(link, "Not adding a blocking route since distributed prefix is /64");
     320                 :            : 
     321                 :          0 :                 r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix.in6,
     322                 :            :                                                pd_prefix_len,
     323                 :            :                                                lifetime_preferred,
     324                 :            :                                                lifetime_valid);
     325   [ #  #  #  # ]:          0 :                 if (r < 0 && r != -EAGAIN)
     326                 :          0 :                         return r;
     327                 :            : 
     328         [ #  # ]:          0 :                 if (r >= 0)
     329                 :          0 :                         i = ITERATOR_FIRST;
     330                 :            :         }
     331                 :            : 
     332                 :          0 :         return 0;
     333                 :            : }
     334                 :            : 
     335                 :          0 : int dhcp6_request_prefix_delegation(Link *link) {
     336                 :            :         Link *l;
     337                 :            :         Iterator i;
     338                 :            : 
     339   [ #  #  #  # ]:          0 :         assert_return(link, -EINVAL);
     340   [ #  #  #  # ]:          0 :         assert_return(link->manager, -EOPNOTSUPP);
     341                 :            : 
     342         [ #  # ]:          0 :         if (dhcp6_get_prefix_delegation(link) <= 0)
     343                 :          0 :                 return 0;
     344                 :            : 
     345   [ #  #  #  # ]:          0 :         log_link_debug(link, "Requesting DHCPv6 prefixes to be delegated for new link");
     346                 :            : 
     347         [ #  # ]:          0 :         HASHMAP_FOREACH(l, link->manager->links, i) {
     348                 :            :                 int r, enabled;
     349                 :            : 
     350         [ #  # ]:          0 :                 if (l == link)
     351                 :          0 :                         continue;
     352                 :            : 
     353         [ #  # ]:          0 :                 if (!l->dhcp6_client)
     354                 :          0 :                         continue;
     355                 :            : 
     356                 :          0 :                 r = sd_dhcp6_client_get_prefix_delegation(l->dhcp6_client, &enabled);
     357         [ #  # ]:          0 :                 if (r < 0) {
     358   [ #  #  #  # ]:          0 :                         log_link_warning_errno(l, r, "Cannot get prefix delegation when adding new link");
     359                 :          0 :                         continue;
     360                 :            :                 }
     361                 :            : 
     362         [ #  # ]:          0 :                 if (enabled == 0) {
     363                 :          0 :                         r = sd_dhcp6_client_set_prefix_delegation(l->dhcp6_client, 1);
     364         [ #  # ]:          0 :                         if (r < 0) {
     365   [ #  #  #  # ]:          0 :                                 log_link_warning_errno(l, r, "Cannot enable prefix delegation when adding new link");
     366                 :          0 :                                 continue;
     367                 :            :                         }
     368                 :            :                 }
     369                 :            : 
     370                 :          0 :                 r = sd_dhcp6_client_is_running(l->dhcp6_client);
     371         [ #  # ]:          0 :                 if (r <= 0)
     372                 :          0 :                         continue;
     373                 :            : 
     374         [ #  # ]:          0 :                 if (enabled != 0) {
     375   [ #  #  #  # ]:          0 :                         log_link_debug(l, "Requesting re-assignment of delegated prefixes after adding new link");
     376                 :          0 :                         (void) dhcp6_lease_pd_prefix_acquired(l->dhcp6_client, l);
     377                 :            : 
     378                 :          0 :                         continue;
     379                 :            :                 }
     380                 :            : 
     381                 :          0 :                 r = sd_dhcp6_client_stop(l->dhcp6_client);
     382         [ #  # ]:          0 :                 if (r < 0) {
     383   [ #  #  #  # ]:          0 :                         log_link_warning_errno(l, r, "Cannot stop DHCPv6 prefix delegation client after adding new link");
     384                 :          0 :                         continue;
     385                 :            :                 }
     386                 :            : 
     387                 :          0 :                 r = sd_dhcp6_client_start(l->dhcp6_client);
     388         [ #  # ]:          0 :                 if (r < 0) {
     389   [ #  #  #  # ]:          0 :                         log_link_warning_errno(l, r, "Cannot restart DHCPv6 prefix delegation client after adding new link");
     390                 :          0 :                         continue;
     391                 :            :                 }
     392                 :            : 
     393   [ #  #  #  # ]:          0 :                 log_link_debug(l, "Restarted DHCPv6 client to acquire prefix delegations after adding new link");
     394                 :            :         }
     395                 :            : 
     396                 :          0 :         return 0;
     397                 :            : }
     398                 :            : 
     399                 :          0 : static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
     400                 :            :         int r;
     401                 :            : 
     402         [ #  # ]:          0 :         assert(link);
     403                 :            : 
     404   [ #  #  #  # ]:          0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     405                 :          0 :                 return 1;
     406                 :            : 
     407                 :          0 :         r = sd_netlink_message_get_errno(m);
     408   [ #  #  #  # ]:          0 :         if (r < 0 && r != -EEXIST) {
     409   [ #  #  #  # ]:          0 :                 log_link_error_errno(link, r, "Could not set DHCPv6 address: %m");
     410                 :          0 :                 link_enter_failed(link);
     411                 :          0 :                 return 1;
     412         [ #  # ]:          0 :         } else if (r >= 0)
     413                 :          0 :                 (void) manager_rtnl_process_address(rtnl, m, link->manager);
     414                 :            : 
     415                 :          0 :         r = link_request_set_routes(link);
     416         [ #  # ]:          0 :         if (r < 0) {
     417                 :          0 :                 link_enter_failed(link);
     418                 :          0 :                 return 1;
     419                 :            :         }
     420                 :            : 
     421                 :          0 :         return 1;
     422                 :            : }
     423                 :            : 
     424                 :          0 : static int dhcp6_address_change(
     425                 :            :                 Link *link,
     426                 :            :                 struct in6_addr *ip6_addr,
     427                 :            :                 uint32_t lifetime_preferred,
     428                 :            :                 uint32_t lifetime_valid) {
     429                 :            : 
     430                 :          0 :         _cleanup_(address_freep) Address *addr = NULL;
     431                 :          0 :         _cleanup_free_ char *buffer = NULL;
     432                 :            :         int r;
     433                 :            : 
     434                 :          0 :         r = address_new(&addr);
     435         [ #  # ]:          0 :         if (r < 0)
     436                 :          0 :                 return r;
     437                 :            : 
     438                 :          0 :         addr->family = AF_INET6;
     439                 :          0 :         addr->in_addr.in6 = *ip6_addr;
     440                 :          0 :         addr->flags = IFA_F_NOPREFIXROUTE;
     441                 :          0 :         addr->prefixlen = 128;
     442                 :          0 :         addr->cinfo.ifa_prefered = lifetime_preferred;
     443                 :          0 :         addr->cinfo.ifa_valid = lifetime_valid;
     444                 :            : 
     445                 :          0 :         (void) in_addr_to_string(addr->family, &addr->in_addr, &buffer);
     446   [ #  #  #  # ]:          0 :         log_link_info(link,
     447                 :            :                       "DHCPv6 address %s/%d timeout preferred %d valid %d",
     448                 :            :                       strnull(buffer), addr->prefixlen, lifetime_preferred, lifetime_valid);
     449                 :            : 
     450                 :          0 :         r = address_configure(addr, link, dhcp6_address_handler, true);
     451         [ #  # ]:          0 :         if (r < 0)
     452   [ #  #  #  # ]:          0 :                 return log_link_warning_errno(link, r, "Could not assign DHCPv6 address: %m");
     453                 :            : 
     454                 :          0 :         return 0;
     455                 :            : }
     456                 :            : 
     457                 :          0 : static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) {
     458                 :            :         int r;
     459                 :            :         sd_dhcp6_lease *lease;
     460                 :            :         struct in6_addr ip6_addr;
     461                 :            :         uint32_t lifetime_preferred, lifetime_valid;
     462                 :            : 
     463                 :          0 :         r = sd_dhcp6_client_get_lease(client, &lease);
     464         [ #  # ]:          0 :         if (r < 0)
     465                 :          0 :                 return r;
     466                 :            : 
     467                 :          0 :         sd_dhcp6_lease_reset_address_iter(lease);
     468                 :            : 
     469         [ #  # ]:          0 :         while (sd_dhcp6_lease_get_address(lease, &ip6_addr,
     470                 :            :                                                  &lifetime_preferred,
     471                 :            :                                                  &lifetime_valid) >= 0) {
     472                 :            : 
     473                 :          0 :                 r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid);
     474         [ #  # ]:          0 :                 if (r < 0)
     475                 :          0 :                         return r;
     476                 :            :         }
     477                 :            : 
     478                 :          0 :         return 0;
     479                 :            : }
     480                 :            : 
     481                 :          0 : static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
     482                 :            :         int r;
     483                 :          0 :         Link *link = userdata;
     484                 :            : 
     485         [ #  # ]:          0 :         assert(link);
     486         [ #  # ]:          0 :         assert(link->network);
     487                 :            : 
     488   [ #  #  #  # ]:          0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     489                 :          0 :                 return;
     490                 :            : 
     491   [ #  #  #  # ]:          0 :         switch(event) {
     492                 :          0 :         case SD_DHCP6_CLIENT_EVENT_STOP:
     493                 :            :         case SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE:
     494                 :            :         case SD_DHCP6_CLIENT_EVENT_RETRANS_MAX:
     495         [ #  # ]:          0 :                 if (sd_dhcp6_client_get_lease(client, NULL) >= 0)
     496   [ #  #  #  # ]:          0 :                         log_link_warning(link, "DHCPv6 lease lost");
     497                 :            : 
     498                 :          0 :                 (void) dhcp6_lease_pd_prefix_lost(client, link);
     499                 :          0 :                 (void) dhcp6_prefix_remove_all(link->manager, link);
     500                 :            : 
     501                 :          0 :                 link_dirty(link);
     502                 :          0 :                 link->dhcp6_configured = false;
     503                 :          0 :                 break;
     504                 :            : 
     505                 :          0 :         case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
     506                 :          0 :                 r = dhcp6_lease_address_acquired(client, link);
     507         [ #  # ]:          0 :                 if (r < 0) {
     508                 :          0 :                         link_enter_failed(link);
     509                 :          0 :                         return;
     510                 :            :                 }
     511                 :            : 
     512                 :          0 :                 r = dhcp6_lease_pd_prefix_acquired(client, link);
     513         [ #  # ]:          0 :                 if (r < 0)
     514   [ #  #  #  # ]:          0 :                         log_link_debug(link, "DHCPv6 did not receive prefixes to delegate");
     515                 :            : 
     516                 :            :                 _fallthrough_;
     517                 :            :         case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
     518                 :          0 :                 r = dhcp6_lease_information_acquired(client, link);
     519         [ #  # ]:          0 :                 if (r < 0) {
     520                 :          0 :                         link_enter_failed(link);
     521                 :          0 :                         return;
     522                 :            :                 }
     523                 :            : 
     524                 :          0 :                 link_dirty(link);
     525                 :          0 :                 link->dhcp6_configured = true;
     526                 :          0 :                 break;
     527                 :            : 
     528                 :          0 :         default:
     529         [ #  # ]:          0 :                 if (event < 0)
     530   [ #  #  #  # ]:          0 :                         log_link_warning_errno(link, event, "DHCPv6 error: %m");
     531                 :            :                 else
     532   [ #  #  #  # ]:          0 :                         log_link_warning(link, "DHCPv6 unknown event: %d", event);
     533                 :          0 :                 return;
     534                 :            :         }
     535                 :            : 
     536                 :          0 :         link_check_ready(link);
     537                 :            : }
     538                 :            : 
     539                 :          0 : int dhcp6_request_address(Link *link, int ir) {
     540                 :            :         int r, inf_req, pd;
     541                 :            :         bool running;
     542                 :            : 
     543         [ #  # ]:          0 :         assert(link);
     544         [ #  # ]:          0 :         assert(link->dhcp6_client);
     545         [ #  # ]:          0 :         assert(link->network);
     546         [ #  # ]:          0 :         assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0);
     547                 :            : 
     548                 :          0 :         r = sd_dhcp6_client_is_running(link->dhcp6_client);
     549         [ #  # ]:          0 :         if (r < 0)
     550                 :          0 :                 return r;
     551                 :            :         else
     552                 :          0 :                 running = r;
     553                 :            : 
     554                 :          0 :         r = sd_dhcp6_client_get_prefix_delegation(link->dhcp6_client, &pd);
     555         [ #  # ]:          0 :         if (r < 0)
     556                 :          0 :                 return r;
     557                 :            : 
     558   [ #  #  #  #  :          0 :         if (pd && ir && link->network->dhcp6_force_pd_other_information) {
                   #  # ]
     559   [ #  #  #  # ]:          0 :                 log_link_debug(link, "Enabling managed mode to request DHCPv6 PD with 'Other Information' set");
     560                 :            : 
     561                 :          0 :                 r = sd_dhcp6_client_set_address_request(link->dhcp6_client,
     562                 :            :                                                         false);
     563         [ #  # ]:          0 :                 if (r < 0 )
     564                 :          0 :                         return r;
     565                 :            : 
     566                 :          0 :                 ir = false;
     567                 :            :         }
     568                 :            : 
     569         [ #  # ]:          0 :         if (running) {
     570                 :          0 :                 r = sd_dhcp6_client_get_information_request(link->dhcp6_client, &inf_req);
     571         [ #  # ]:          0 :                 if (r < 0)
     572                 :          0 :                         return r;
     573                 :            : 
     574         [ #  # ]:          0 :                 if (inf_req == ir)
     575                 :          0 :                         return 0;
     576                 :            : 
     577                 :          0 :                 r = sd_dhcp6_client_stop(link->dhcp6_client);
     578         [ #  # ]:          0 :                 if (r < 0)
     579                 :          0 :                         return r;
     580                 :            :         } else {
     581                 :          0 :                 r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address);
     582         [ #  # ]:          0 :                 if (r < 0)
     583                 :          0 :                         return r;
     584                 :            :         }
     585                 :            : 
     586                 :          0 :         r = sd_dhcp6_client_set_information_request(link->dhcp6_client, ir);
     587         [ #  # ]:          0 :         if (r < 0)
     588                 :          0 :                 return r;
     589                 :            : 
     590                 :          0 :         r = sd_dhcp6_client_start(link->dhcp6_client);
     591         [ #  # ]:          0 :         if (r < 0)
     592                 :          0 :                 return r;
     593                 :            : 
     594                 :          0 :         return 0;
     595                 :            : }
     596                 :            : 
     597                 :          0 : static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) {
     598                 :          0 :         _cleanup_free_ char *hostname = NULL;
     599                 :            :         const char *hn;
     600                 :            :         int r;
     601                 :            : 
     602         [ #  # ]:          0 :         assert(link);
     603                 :            : 
     604         [ #  # ]:          0 :         if (!link->network->dhcp_send_hostname)
     605                 :          0 :                 hn = NULL;
     606         [ #  # ]:          0 :         else if (link->network->dhcp_hostname)
     607                 :          0 :                 hn = link->network->dhcp_hostname;
     608                 :            :         else {
     609                 :          0 :                 r = gethostname_strict(&hostname);
     610   [ #  #  #  # ]:          0 :                 if (r < 0 && r != -ENXIO) /* ENXIO: no hostname set or hostname is "localhost" */
     611                 :          0 :                         return r;
     612                 :            : 
     613                 :          0 :                 hn = hostname;
     614                 :            :         }
     615                 :            : 
     616                 :          0 :         r = sd_dhcp6_client_set_fqdn(client, hn);
     617   [ #  #  #  # ]:          0 :         if (r == -EINVAL && hostname)
     618                 :            :                 /* Ignore error when the machine's hostname is not suitable to send in DHCP packet. */
     619   [ #  #  #  # ]:          0 :                 log_link_warning_errno(link, r, "DHCP6 CLIENT: Failed to set hostname from kernel hostname, ignoring: %m");
     620         [ #  # ]:          0 :         else if (r < 0)
     621   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set hostname: %m");
     622                 :            : 
     623                 :          0 :         return 0;
     624                 :            : }
     625                 :            : 
     626                 :          0 : int dhcp6_configure(Link *link) {
     627                 :          0 :         _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
     628                 :            :         const DUID *duid;
     629                 :            :         int r;
     630                 :            : 
     631         [ #  # ]:          0 :         assert(link);
     632         [ #  # ]:          0 :         assert(link->network);
     633                 :            : 
     634         [ #  # ]:          0 :         if (link->dhcp6_client)
     635                 :          0 :                 return 0;
     636                 :            : 
     637                 :          0 :         r = sd_dhcp6_client_new(&client);
     638         [ #  # ]:          0 :         if (r == -ENOMEM)
     639                 :          0 :                 return log_oom();
     640         [ #  # ]:          0 :         if (r < 0)
     641   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to create DHCP6 client: %m");
     642                 :            : 
     643                 :          0 :         r = sd_dhcp6_client_attach_event(client, NULL, 0);
     644         [ #  # ]:          0 :         if (r < 0)
     645   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to attach event: %m");
     646                 :            : 
     647                 :          0 :         r = sd_dhcp6_client_set_mac(client,
     648                 :          0 :                                     (const uint8_t *) &link->mac,
     649                 :            :                                     sizeof (link->mac), ARPHRD_ETHER);
     650         [ #  # ]:          0 :         if (r < 0)
     651   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set MAC address: %m");
     652                 :            : 
     653         [ #  # ]:          0 :         if (link->network->iaid_set) {
     654                 :          0 :                 r = sd_dhcp6_client_set_iaid(client, link->network->iaid);
     655         [ #  # ]:          0 :                 if (r < 0)
     656   [ #  #  #  # ]:          0 :                         return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m");
     657                 :            :         }
     658                 :            : 
     659                 :          0 :         duid = link_get_duid(link);
     660   [ #  #  #  # ]:          0 :         if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0)
     661                 :          0 :                 r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time);
     662                 :            :         else
     663                 :          0 :                 r = sd_dhcp6_client_set_duid(client,
     664                 :          0 :                                              duid->type,
     665                 :          0 :                                              duid->raw_data_len > 0 ? duid->raw_data : NULL,
     666         [ #  # ]:          0 :                                              duid->raw_data_len);
     667         [ #  # ]:          0 :         if (r < 0)
     668   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m");
     669                 :            : 
     670                 :          0 :         r = dhcp6_set_hostname(client, link);
     671         [ #  # ]:          0 :         if (r < 0)
     672                 :          0 :                 return r;
     673                 :            : 
     674                 :          0 :         r = sd_dhcp6_client_set_ifindex(client, link->ifindex);
     675         [ #  # ]:          0 :         if (r < 0)
     676   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set ifindex: %m");
     677                 :            : 
     678         [ #  # ]:          0 :         if (link->network->rapid_commit) {
     679                 :          0 :                 r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT);
     680         [ #  # ]:          0 :                 if (r < 0)
     681   [ #  #  #  # ]:          0 :                         return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set request flag for rapid commit: %m");
     682                 :            :         }
     683                 :            : 
     684                 :          0 :         r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link);
     685         [ #  # ]:          0 :         if (r < 0)
     686   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set callback: %m");
     687                 :            : 
     688         [ #  # ]:          0 :         if (dhcp6_enable_prefix_delegation(link)) {
     689                 :          0 :                 r = sd_dhcp6_client_set_prefix_delegation(client, true);
     690         [ #  # ]:          0 :                 if (r < 0)
     691   [ #  #  #  # ]:          0 :                         return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set prefix delegation: %m");
     692                 :            :         }
     693                 :            : 
     694                 :          0 :         link->dhcp6_client = TAKE_PTR(client);
     695                 :            : 
     696                 :          0 :         return 0;
     697                 :            : }
     698                 :            : 
     699                 :          0 : static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
     700   [ #  #  #  # ]:          0 :         assert_return(m, NULL);
     701   [ #  #  #  # ]:          0 :         assert_return(addr, NULL);
     702                 :            : 
     703                 :          0 :         return hashmap_get(m->dhcp6_prefixes, addr);
     704                 :            : }
     705                 :            : 
     706                 :          0 : static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
     707                 :            :         int r;
     708                 :            : 
     709         [ #  # ]:          0 :         assert(link);
     710                 :            : 
     711   [ #  #  #  # ]:          0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     712                 :          0 :                 return 1;
     713                 :            : 
     714                 :          0 :         r = sd_netlink_message_get_errno(m);
     715   [ #  #  #  # ]:          0 :         if (r < 0 && r != -EEXIST) {
     716   [ #  #  #  # ]:          0 :                 log_link_debug_errno(link, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
     717                 :          0 :                 link_enter_failed(link);
     718                 :          0 :                 return 1;
     719                 :            :         }
     720                 :            : 
     721                 :          0 :         return 1;
     722                 :            : }
     723                 :            : 
     724                 :          0 : static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
     725                 :          0 :         _cleanup_free_ struct in6_addr *a = NULL;
     726                 :          0 :         _cleanup_free_ char *buf = NULL;
     727                 :            :         Link *assigned_link;
     728                 :            :         Route *route;
     729                 :            :         int r;
     730                 :            : 
     731   [ #  #  #  # ]:          0 :         assert_return(m, -EINVAL);
     732   [ #  #  #  # ]:          0 :         assert_return(addr, -EINVAL);
     733                 :            : 
     734                 :          0 :         r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
     735                 :            :                       NULL, 0, 0, 0, &route);
     736         [ #  # ]:          0 :         if (r < 0)
     737                 :          0 :                 return r;
     738                 :            : 
     739                 :          0 :         r = route_configure(route, link, dhcp6_route_add_handler);
     740         [ #  # ]:          0 :         if (r < 0)
     741                 :          0 :                 return r;
     742                 :            : 
     743                 :          0 :         (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
     744   [ #  #  #  # ]:          0 :         log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
     745                 :            : 
     746                 :          0 :         assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
     747         [ #  # ]:          0 :         if (assigned_link) {
     748         [ #  # ]:          0 :                 assert(assigned_link == link);
     749                 :          0 :                 return 0;
     750                 :            :         }
     751                 :            : 
     752                 :          0 :         a = newdup(struct in6_addr, addr, 1);
     753         [ #  # ]:          0 :         if (!a)
     754                 :          0 :                 return -ENOMEM;
     755                 :            : 
     756                 :          0 :         r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
     757         [ #  # ]:          0 :         if (r < 0)
     758                 :          0 :                 return r;
     759                 :            : 
     760                 :          0 :         r = hashmap_put(m->dhcp6_prefixes, a, link);
     761         [ #  # ]:          0 :         if (r < 0)
     762                 :          0 :                 return r;
     763                 :            : 
     764                 :          0 :         TAKE_PTR(a);
     765                 :          0 :         link_ref(link);
     766                 :          0 :         return 0;
     767                 :            : }
     768                 :            : 
     769                 :          0 : static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
     770                 :            :         int r;
     771                 :            : 
     772         [ #  # ]:          0 :         assert(link);
     773                 :            : 
     774   [ #  #  #  # ]:          0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     775                 :          0 :                 return 1;
     776                 :            : 
     777                 :          0 :         r = sd_netlink_message_get_errno(m);
     778         [ #  # ]:          0 :         if (r < 0) {
     779   [ #  #  #  # ]:          0 :                 log_link_debug_errno(link, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
     780                 :          0 :                 link_enter_failed(link);
     781                 :          0 :                 return 1;
     782                 :            :         }
     783                 :            : 
     784                 :          0 :         return 1;
     785                 :            : }
     786                 :            : 
     787                 :          0 : int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
     788                 :          0 :         _cleanup_free_ struct in6_addr *a = NULL;
     789                 :          0 :         _cleanup_(link_unrefp) Link *l = NULL;
     790                 :          0 :         _cleanup_free_ char *buf = NULL;
     791                 :            :         Route *route;
     792                 :            :         int r;
     793                 :            : 
     794   [ #  #  #  # ]:          0 :         assert_return(m, -EINVAL);
     795   [ #  #  #  # ]:          0 :         assert_return(addr, -EINVAL);
     796                 :            : 
     797                 :          0 :         l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
     798         [ #  # ]:          0 :         if (!l)
     799                 :          0 :                 return -EINVAL;
     800                 :            : 
     801                 :          0 :         (void) sd_radv_remove_prefix(l->radv, addr, 64);
     802                 :          0 :         r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, NULL, 0, 0, 0, &route);
     803         [ #  # ]:          0 :         if (r < 0)
     804                 :          0 :                 return r;
     805                 :            : 
     806                 :          0 :         r = route_remove(route, l, dhcp6_prefix_remove_handler);
     807         [ #  # ]:          0 :         if (r < 0)
     808                 :          0 :                 return r;
     809                 :            : 
     810                 :          0 :         (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
     811   [ #  #  #  # ]:          0 :         log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
     812                 :            : 
     813                 :          0 :         return 0;
     814                 :            : }
     815                 :            : 
     816                 :          0 : static int dhcp6_prefix_remove_all(Manager *m, Link *link) {
     817                 :            :         struct in6_addr *addr;
     818                 :            :         Iterator i;
     819                 :            :         Link *l;
     820                 :            : 
     821   [ #  #  #  # ]:          0 :         assert_return(m, -EINVAL);
     822   [ #  #  #  # ]:          0 :         assert_return(link, -EINVAL);
     823                 :            : 
     824         [ #  # ]:          0 :         HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
     825         [ #  # ]:          0 :                 if (l == link)
     826                 :          0 :                         (void) dhcp6_prefix_remove(m, addr);
     827                 :            : 
     828                 :          0 :         return 0;
     829                 :            : }

Generated by: LCOV version 1.14