LCOV - code coverage report
Current view: top level - network - networkd-neighbor.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 0 275 0.0 %
Date: 2019-08-22 15:41:25 Functions: 0 17 0.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "sd-netlink.h"
       4             : 
       5             : #include "alloc-util.h"
       6             : #include "conf-parser.h"
       7             : #include "ether-addr-util.h"
       8             : #include "hashmap.h"
       9             : #include "in-addr-util.h"
      10             : #include "netlink-util.h"
      11             : #include "networkd-link.h"
      12             : #include "networkd-manager.h"
      13             : #include "networkd-neighbor.h"
      14             : #include "set.h"
      15             : 
      16           0 : void neighbor_free(Neighbor *neighbor) {
      17           0 :         if (!neighbor)
      18           0 :                 return;
      19             : 
      20           0 :         if (neighbor->network) {
      21           0 :                 LIST_REMOVE(neighbors, neighbor->network->neighbors, neighbor);
      22           0 :                 assert(neighbor->network->n_neighbors > 0);
      23           0 :                 neighbor->network->n_neighbors--;
      24             : 
      25           0 :                 if (neighbor->section)
      26           0 :                         hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section);
      27             :         }
      28             : 
      29           0 :         network_config_section_free(neighbor->section);
      30             : 
      31           0 :         if (neighbor->link) {
      32           0 :                 set_remove(neighbor->link->neighbors, neighbor);
      33           0 :                 set_remove(neighbor->link->neighbors_foreign, neighbor);
      34             :         }
      35             : 
      36           0 :         free(neighbor);
      37             : }
      38             : 
      39           0 : static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) {
      40           0 :         _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
      41           0 :         _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
      42             :         int r;
      43             : 
      44           0 :         assert(network);
      45           0 :         assert(ret);
      46           0 :         assert(!!filename == (section_line > 0));
      47             : 
      48           0 :         if (filename) {
      49           0 :                 r = network_config_section_new(filename, section_line, &n);
      50           0 :                 if (r < 0)
      51           0 :                         return r;
      52             : 
      53           0 :                 neighbor = hashmap_get(network->neighbors_by_section, n);
      54           0 :                 if (neighbor) {
      55           0 :                         *ret = TAKE_PTR(neighbor);
      56             : 
      57           0 :                         return 0;
      58             :                 }
      59             :         }
      60             : 
      61           0 :         neighbor = new(Neighbor, 1);
      62           0 :         if (!neighbor)
      63           0 :                 return -ENOMEM;
      64             : 
      65           0 :         *neighbor = (Neighbor) {
      66             :                 .network = network,
      67             :                 .family = AF_UNSPEC,
      68             :         };
      69             : 
      70           0 :         LIST_APPEND(neighbors, network->neighbors, neighbor);
      71           0 :         network->n_neighbors++;
      72             : 
      73           0 :         if (filename) {
      74           0 :                 neighbor->section = TAKE_PTR(n);
      75             : 
      76           0 :                 r = hashmap_ensure_allocated(&network->neighbors_by_section, &network_config_hash_ops);
      77           0 :                 if (r < 0)
      78           0 :                         return r;
      79             : 
      80           0 :                 r = hashmap_put(network->neighbors_by_section, neighbor->section, neighbor);
      81           0 :                 if (r < 0)
      82           0 :                         return r;
      83             :         }
      84             : 
      85           0 :         *ret = TAKE_PTR(neighbor);
      86             : 
      87           0 :         return 0;
      88             : }
      89             : 
      90           0 : static int neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
      91             :         int r;
      92             : 
      93           0 :         assert(m);
      94           0 :         assert(link);
      95           0 :         assert(link->neighbor_messages > 0);
      96             : 
      97           0 :         link->neighbor_messages--;
      98             : 
      99           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     100           0 :                 return 1;
     101             : 
     102           0 :         r = sd_netlink_message_get_errno(m);
     103           0 :         if (r < 0 && r != -EEXIST)
     104             :                 /* Neighbor may not exist yet. So, do not enter failed state here. */
     105           0 :                 log_link_warning_errno(link, r, "Could not set neighbor, ignoring: %m");
     106             : 
     107           0 :         if (link->neighbor_messages == 0) {
     108           0 :                 log_link_debug(link, "Neighbors set");
     109           0 :                 link->neighbors_configured = true;
     110           0 :                 link_check_ready(link);
     111             :         }
     112             : 
     113           0 :         return 1;
     114             : }
     115             : 
     116           0 : int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) {
     117           0 :         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
     118             :         int r;
     119             : 
     120           0 :         assert(neighbor);
     121           0 :         assert(link);
     122           0 :         assert(link->ifindex > 0);
     123           0 :         assert(link->manager);
     124           0 :         assert(link->manager->rtnl);
     125             : 
     126           0 :         r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH,
     127             :                                           link->ifindex, neighbor->family);
     128           0 :         if (r < 0)
     129           0 :                 return log_error_errno(r, "Could not allocate RTM_NEWNEIGH message: %m");
     130             : 
     131           0 :         r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT);
     132           0 :         if (r < 0)
     133           0 :                 return log_error_errno(r, "Could not set state: %m");
     134             : 
     135           0 :         r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE);
     136           0 :         if (r < 0)
     137           0 :                 return log_error_errno(r, "Could not set flags: %m");
     138             : 
     139           0 :         r = sd_netlink_message_append_data(req, NDA_LLADDR, &neighbor->lladdr, neighbor->lladdr_size);
     140           0 :         if (r < 0)
     141           0 :                 return log_error_errno(r, "Could not append NDA_LLADDR attribute: %m");
     142             : 
     143           0 :         r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr);
     144           0 :         if (r < 0)
     145           0 :                 return log_error_errno(r, "Could not append NDA_DST attribute: %m");
     146             : 
     147           0 :         r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_configure_handler,
     148             :                                link_netlink_destroy_callback, link);
     149           0 :         if (r < 0)
     150           0 :                 return log_error_errno(r, "Could not send rtnetlink message: %m");
     151             : 
     152           0 :         link->neighbor_messages++;
     153           0 :         link_ref(link);
     154             : 
     155           0 :         r = neighbor_add(link, neighbor->family, &neighbor->in_addr, &neighbor->lladdr, neighbor->lladdr_size, NULL);
     156           0 :         if (r < 0)
     157           0 :                 return log_link_error_errno(link, r, "Could not add neighbor: %m");
     158             : 
     159           0 :         return 0;
     160             : }
     161             : 
     162           0 : static int neighbor_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
     163             :         int r;
     164             : 
     165           0 :         assert(m);
     166           0 :         assert(link);
     167             : 
     168           0 :         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
     169           0 :                 return 1;
     170             : 
     171           0 :         r = sd_netlink_message_get_errno(m);
     172           0 :         if (r < 0 && r != -ESRCH)
     173             :                 /* Neighbor may not exist because it already got deleted, ignore that. */
     174           0 :                 log_link_warning_errno(link, r, "Could not remove neighbor: %m");
     175             : 
     176           0 :         return 1;
     177             : }
     178             : 
     179           0 : int neighbor_remove(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback) {
     180           0 :         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
     181             :         int r;
     182             : 
     183           0 :         assert(neighbor);
     184           0 :         assert(link);
     185           0 :         assert(link->ifindex > 0);
     186           0 :         assert(link->manager);
     187           0 :         assert(link->manager->rtnl);
     188             : 
     189           0 :         r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_DELNEIGH,
     190             :                                           link->ifindex, neighbor->family);
     191           0 :         if (r < 0)
     192           0 :                 return log_error_errno(r, "Could not allocate RTM_DELNEIGH message: %m");
     193             : 
     194           0 :         r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr);
     195           0 :         if (r < 0)
     196           0 :                 return log_error_errno(r, "Could not append NDA_DST attribute: %m");
     197             : 
     198           0 :         r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_remove_handler,
     199             :                                link_netlink_destroy_callback, link);
     200           0 :         if (r < 0)
     201           0 :                 return log_error_errno(r, "Could not send rtnetlink message: %m");
     202             : 
     203           0 :         link_ref(link);
     204             : 
     205           0 :         return 0;
     206             : }
     207             : 
     208           0 : static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) {
     209           0 :         assert(neighbor);
     210             : 
     211           0 :         siphash24_compress(&neighbor->family, sizeof(neighbor->family), state);
     212             : 
     213           0 :         switch (neighbor->family) {
     214           0 :         case AF_INET:
     215             :         case AF_INET6:
     216             :                 /* Equality of neighbors are given by the pair (addr,lladdr) */
     217           0 :                 siphash24_compress(&neighbor->in_addr, FAMILY_ADDRESS_SIZE(neighbor->family), state);
     218           0 :                 siphash24_compress(&neighbor->lladdr, neighbor->lladdr_size, state);
     219           0 :                 break;
     220           0 :         default:
     221             :                 /* treat any other address family as AF_UNSPEC */
     222           0 :                 break;
     223             :         }
     224           0 : }
     225             : 
     226           0 : static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) {
     227             :         int r;
     228             : 
     229           0 :         r = CMP(a->family, b->family);
     230           0 :         if (r != 0)
     231           0 :                 return r;
     232             : 
     233           0 :         r = CMP(a->lladdr_size, b->lladdr_size);
     234           0 :         if (r != 0)
     235           0 :                 return r;
     236             : 
     237           0 :         switch (a->family) {
     238           0 :         case AF_INET:
     239             :         case AF_INET6:
     240           0 :                 r = memcmp(&a->in_addr, &b->in_addr, FAMILY_ADDRESS_SIZE(a->family));
     241           0 :                 if (r != 0)
     242           0 :                         return r;
     243             :         }
     244             : 
     245           0 :         return memcmp(&a->lladdr, &b->lladdr, a->lladdr_size);
     246             : }
     247             : 
     248             : DEFINE_PRIVATE_HASH_OPS(neighbor_hash_ops, Neighbor, neighbor_hash_func, neighbor_compare_func);
     249             : 
     250           0 : int neighbor_get(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
     251             :         Neighbor neighbor, *existing;
     252             : 
     253           0 :         assert(link);
     254           0 :         assert(addr);
     255           0 :         assert(lladdr);
     256             : 
     257           0 :         neighbor = (Neighbor) {
     258             :                 .family = family,
     259           0 :                 .in_addr = *addr,
     260           0 :                 .lladdr = *lladdr,
     261             :                 .lladdr_size = lladdr_size,
     262             :         };
     263             : 
     264           0 :         existing = set_get(link->neighbors, &neighbor);
     265           0 :         if (existing) {
     266           0 :                 if (ret)
     267           0 :                         *ret = existing;
     268           0 :                 return 1;
     269             :         }
     270             : 
     271           0 :         existing = set_get(link->neighbors_foreign, &neighbor);
     272           0 :         if (existing) {
     273           0 :                 if (ret)
     274           0 :                         *ret = existing;
     275           0 :                 return 0;
     276             :         }
     277             : 
     278           0 :         return -ENOENT;
     279             : }
     280             : 
     281           0 : static int neighbor_add_internal(Link *link, Set **neighbors, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
     282           0 :         _cleanup_(neighbor_freep) Neighbor *neighbor = NULL;
     283             :         int r;
     284             : 
     285           0 :         assert(link);
     286           0 :         assert(neighbors);
     287           0 :         assert(addr);
     288           0 :         assert(lladdr);
     289             : 
     290           0 :         neighbor = new(Neighbor, 1);
     291           0 :         if (!neighbor)
     292           0 :                 return -ENOMEM;
     293             : 
     294           0 :         *neighbor = (Neighbor) {
     295             :                 .family = family,
     296           0 :                 .in_addr = *addr,
     297           0 :                 .lladdr = *lladdr,
     298             :                 .lladdr_size = lladdr_size,
     299             :         };
     300             : 
     301           0 :         r = set_ensure_allocated(neighbors, &neighbor_hash_ops);
     302           0 :         if (r < 0)
     303           0 :                 return r;
     304             : 
     305           0 :         r = set_put(*neighbors, neighbor);
     306           0 :         if (r < 0)
     307           0 :                 return r;
     308           0 :         if (r == 0)
     309           0 :                 return -EEXIST;
     310             : 
     311           0 :         neighbor->link = link;
     312             : 
     313           0 :         if (ret)
     314           0 :                 *ret = neighbor;
     315             : 
     316           0 :         neighbor = NULL;
     317             : 
     318           0 :         return 0;
     319             : }
     320             : 
     321           0 : int neighbor_add(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
     322             :         Neighbor *neighbor;
     323             :         int r;
     324             : 
     325           0 :         r = neighbor_get(link, family, addr, lladdr, lladdr_size, &neighbor);
     326           0 :         if (r == -ENOENT) {
     327             :                 /* Neighbor doesn't exist, make a new one */
     328           0 :                 r = neighbor_add_internal(link, &link->neighbors, family, addr, lladdr, lladdr_size, &neighbor);
     329           0 :                 if (r < 0)
     330           0 :                         return r;
     331           0 :         } else if (r == 0) {
     332             :                 /* Neighbor is foreign, claim it as recognized */
     333           0 :                 r = set_ensure_allocated(&link->neighbors, &neighbor_hash_ops);
     334           0 :                 if (r < 0)
     335           0 :                         return r;
     336             : 
     337           0 :                 r = set_put(link->neighbors, neighbor);
     338           0 :                 if (r < 0)
     339           0 :                         return r;
     340             : 
     341           0 :                 set_remove(link->neighbors_foreign, neighbor);
     342           0 :         } else if (r == 1) {
     343             :                 /* Neighbor already exists */
     344             :         } else
     345           0 :                 return r;
     346             : 
     347           0 :         if (ret)
     348           0 :                 *ret = neighbor;
     349           0 :         return 0;
     350             : }
     351             : 
     352           0 : int neighbor_add_foreign(Link *link, int family, const union in_addr_union *addr, const union lladdr_union *lladdr, size_t lladdr_size, Neighbor **ret) {
     353           0 :         return neighbor_add_internal(link, &link->neighbors_foreign, family, addr, lladdr, lladdr_size, ret);
     354             : }
     355             : 
     356           0 : bool neighbor_equal(const Neighbor *n1, const Neighbor *n2) {
     357           0 :         if (n1 == n2)
     358           0 :                 return true;
     359             : 
     360           0 :         if (!n1 || !n2)
     361           0 :                 return false;
     362             : 
     363           0 :         return neighbor_compare_func(n1, n2) == 0;
     364             : }
     365             : 
     366           0 : int neighbor_section_verify(Neighbor *neighbor) {
     367           0 :         if (section_is_invalid(neighbor->section))
     368           0 :                 return -EINVAL;
     369             : 
     370           0 :         if (neighbor->family == AF_UNSPEC)
     371           0 :                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
     372             :                                          "%s: Neighbor section without Address= configured. "
     373             :                                          "Ignoring [Neighbor] section from line %u.",
     374             :                                          neighbor->section->filename, neighbor->section->line);
     375             : 
     376           0 :         if (neighbor->lladdr_size == 0)
     377           0 :                 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
     378             :                                          "%s: Neighbor section without LinkLayerAddress= configured. "
     379             :                                          "Ignoring [Neighbor] section from line %u.",
     380             :                                          neighbor->section->filename, neighbor->section->line);
     381             : 
     382           0 :         return 0;
     383             : }
     384             : 
     385           0 : int config_parse_neighbor_address(
     386             :                 const char *unit,
     387             :                 const char *filename,
     388             :                 unsigned line,
     389             :                 const char *section,
     390             :                 unsigned section_line,
     391             :                 const char *lvalue,
     392             :                 int ltype,
     393             :                 const char *rvalue,
     394             :                 void *data,
     395             :                 void *userdata) {
     396             : 
     397           0 :         Network *network = userdata;
     398           0 :         _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
     399             :         int r;
     400             : 
     401           0 :         assert(filename);
     402           0 :         assert(section);
     403           0 :         assert(lvalue);
     404           0 :         assert(rvalue);
     405           0 :         assert(data);
     406             : 
     407           0 :         r = neighbor_new_static(network, filename, section_line, &n);
     408           0 :         if (r < 0)
     409           0 :                 return r;
     410             : 
     411           0 :         r = in_addr_from_string_auto(rvalue, &n->family, &n->in_addr);
     412           0 :         if (r < 0) {
     413           0 :                 log_syntax(unit, LOG_ERR, filename, line, r, "Neighbor Address is invalid, ignoring assignment: %s", rvalue);
     414           0 :                 return 0;
     415             :         }
     416             : 
     417           0 :         TAKE_PTR(n);
     418             : 
     419           0 :         return 0;
     420             : }
     421             : 
     422           0 : int config_parse_neighbor_lladdr(
     423             :                 const char *unit,
     424             :                 const char *filename,
     425             :                 unsigned line,
     426             :                 const char *section,
     427             :                 unsigned section_line,
     428             :                 const char *lvalue,
     429             :                 int ltype,
     430             :                 const char *rvalue,
     431             :                 void *data,
     432             :                 void *userdata) {
     433             : 
     434           0 :         Network *network = userdata;
     435           0 :         _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
     436             :         int family, r;
     437             : 
     438           0 :         assert(filename);
     439           0 :         assert(section);
     440           0 :         assert(lvalue);
     441           0 :         assert(rvalue);
     442           0 :         assert(data);
     443             : 
     444           0 :         r = neighbor_new_static(network, filename, section_line, &n);
     445           0 :         if (r < 0)
     446           0 :                 return r;
     447             : 
     448           0 :         r = ether_addr_from_string(rvalue, &n->lladdr.mac);
     449           0 :         if (r >= 0)
     450           0 :                 n->lladdr_size = sizeof(n->lladdr.mac);
     451             :         else {
     452           0 :                 r = in_addr_from_string_auto(rvalue, &family, &n->lladdr.ip);
     453           0 :                 if (r < 0) {
     454           0 :                         log_syntax(unit, LOG_ERR, filename, line, r,
     455             :                                    "Neighbor LinkLayerAddress= is invalid, ignoring assignment: %s",
     456             :                                    rvalue);
     457           0 :                         return 0;
     458             :                 }
     459           0 :                 n->lladdr_size = family == AF_INET ? sizeof(n->lladdr.ip.in) : sizeof(n->lladdr.ip.in6);
     460             :         }
     461             : 
     462           0 :         TAKE_PTR(n);
     463             : 
     464           0 :         return 0;
     465             : }
     466             : 
     467           0 : int config_parse_neighbor_hwaddr(
     468             :                 const char *unit,
     469             :                 const char *filename,
     470             :                 unsigned line,
     471             :                 const char *section,
     472             :                 unsigned section_line,
     473             :                 const char *lvalue,
     474             :                 int ltype,
     475             :                 const char *rvalue,
     476             :                 void *data,
     477             :                 void *userdata) {
     478             : 
     479           0 :         Network *network = userdata;
     480           0 :         _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL;
     481             :         int r;
     482             : 
     483           0 :         assert(filename);
     484           0 :         assert(section);
     485           0 :         assert(lvalue);
     486           0 :         assert(rvalue);
     487           0 :         assert(data);
     488             : 
     489           0 :         r = neighbor_new_static(network, filename, section_line, &n);
     490           0 :         if (r < 0)
     491           0 :                 return r;
     492             : 
     493           0 :         r = ether_addr_from_string(rvalue, &n->lladdr.mac);
     494           0 :         if (r < 0) {
     495           0 :                 log_syntax(unit, LOG_ERR, filename, line, r,
     496             :                            "Neighbor MACAddress= is invalid, ignoring assignment: %s", rvalue);
     497           0 :                 return 0;
     498             :         }
     499             : 
     500           0 :         n->lladdr_size = sizeof(n->lladdr.mac);
     501           0 :         TAKE_PTR(n);
     502             : 
     503           0 :         return 0;
     504             : }

Generated by: LCOV version 1.14