LCOV - code coverage report
Current view: top level - network/generator - network-generator.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 565 679 83.2 %
Date: 2019-08-22 15:41:25 Functions: 53 53 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "ether-addr-util.h"
       4             : #include "fd-util.h"
       5             : #include "fileio.h"
       6             : #include "hostname-util.h"
       7             : #include "log.h"
       8             : #include "macro.h"
       9             : #include "network-generator.h"
      10             : #include "parse-util.h"
      11             : #include "proc-cmdline.h"
      12             : #include "socket-util.h"
      13             : #include "string-table.h"
      14             : #include "string-util.h"
      15             : #include "strv.h"
      16             : 
      17             : /*
      18             :   # .network
      19             :   ip={dhcp|on|any|dhcp6|auto6|either6}
      20             :   ip=<interface>:{dhcp|on|any|dhcp6|auto6}[:[<mtu>][:<macaddr>]]
      21             :   ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<mtu>][:<macaddr>]]
      22             :   ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<dns1>][:<dns2>]]
      23             :   rd.route=<net>/<netmask>:<gateway>[:<interface>]
      24             :   nameserver=<IP> [nameserver=<IP> ...]
      25             :   rd.peerdns=0
      26             : 
      27             :   # .link
      28             :   ifname=<interface>:<MAC>
      29             : 
      30             :   # .netdev
      31             :   vlan=<vlanname>:<phydevice>
      32             :   bond=<bondname>[:<bondslaves>:[:<options>[:<mtu>]]]
      33             :   team=<teammaster>:<teamslaves> # not supported
      34             :   bridge=<bridgename>:<ethnames>
      35             : 
      36             :   # ignored
      37             :   bootdev=<interface>
      38             :   BOOTIF=<MAC>
      39             :   rd.bootif=0
      40             :   biosdevname=0
      41             :   rd.neednet=1
      42             : */
      43             : 
      44             : static const char * const dracut_dhcp_type_table[_DHCP_TYPE_MAX] = {
      45             :         [DHCP_TYPE_NONE]    = "none",
      46             :         [DHCP_TYPE_OFF]     = "off",
      47             :         [DHCP_TYPE_ON]      = "on",
      48             :         [DHCP_TYPE_ANY]     = "any",
      49             :         [DHCP_TYPE_DHCP]    = "dhcp",
      50             :         [DHCP_TYPE_DHCP6]   = "dhcp6",
      51             :         [DHCP_TYPE_AUTO6]   = "auto6",
      52             :         [DHCP_TYPE_EITHER6] = "either6",
      53             :         [DHCP_TYPE_IBFT]    = "ibft",
      54             : };
      55             : 
      56          17 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dracut_dhcp_type, DHCPType);
      57             : 
      58             : static const char * const networkd_dhcp_type_table[_DHCP_TYPE_MAX] = {
      59             :         [DHCP_TYPE_NONE]    = "no",
      60             :         [DHCP_TYPE_OFF]     = "no",
      61             :         [DHCP_TYPE_ON]      = "yes",
      62             :         [DHCP_TYPE_ANY]     = "yes",
      63             :         [DHCP_TYPE_DHCP]    = "ipv4",
      64             :         [DHCP_TYPE_DHCP6]   = "ipv6",
      65             :         [DHCP_TYPE_AUTO6]   = "no",   /* TODO: enable other setting? */
      66             :         [DHCP_TYPE_EITHER6] = "ipv6", /* TODO: enable other setting? */
      67             :         [DHCP_TYPE_IBFT]    = "no",
      68             : };
      69             : 
      70          27 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(networkd_dhcp_type, DHCPType);
      71             : 
      72          13 : static Address *address_free(Address *address) {
      73          13 :         if (!address)
      74           0 :                 return NULL;
      75             : 
      76          13 :         if (address->network)
      77          13 :                 LIST_REMOVE(addresses, address->network->addresses, address);
      78             : 
      79          13 :         return mfree(address);
      80             : }
      81             : 
      82          13 : static int address_new(Network *network, int family, unsigned char prefixlen,
      83             :                        union in_addr_union *addr, union in_addr_union *peer, Address **ret) {
      84             :         Address *address;
      85             : 
      86          13 :         assert(network);
      87             : 
      88          13 :         address = new(Address, 1);
      89          13 :         if (!address)
      90           0 :                 return -ENOMEM;
      91             : 
      92          13 :         *address = (Address) {
      93             :                 .family = family,
      94             :                 .prefixlen = prefixlen,
      95          13 :                 .address = *addr,
      96          13 :                 .peer = *peer,
      97             :         };
      98             : 
      99          13 :         LIST_PREPEND(addresses, network->addresses, address);
     100             : 
     101          13 :         address->network = network;
     102             : 
     103          13 :         if (ret)
     104           0 :                 *ret = address;
     105          13 :         return 0;
     106             : }
     107             : 
     108          17 : static Route *route_free(Route *route) {
     109          17 :         if (!route)
     110           0 :                 return NULL;
     111             : 
     112          17 :         if (route->network)
     113          17 :                 LIST_REMOVE(routes, route->network->routes, route);
     114             : 
     115          17 :         return mfree(route);
     116             : }
     117             : 
     118          17 : static int route_new(Network *network, int family, unsigned char prefixlen,
     119             :                      union in_addr_union *dest, union in_addr_union *gateway, Route **ret) {
     120             :         Route *route;
     121             : 
     122          17 :         assert(network);
     123             : 
     124          17 :         route = new(Route, 1);
     125          17 :         if (!route)
     126           0 :                 return -ENOMEM;
     127             : 
     128          17 :         *route = (Route) {
     129             :                 .family = family,
     130             :                 .prefixlen = prefixlen,
     131          17 :                 .dest = dest ? *dest : IN_ADDR_NULL,
     132          17 :                 .gateway = *gateway,
     133             :         };
     134             : 
     135          17 :         LIST_PREPEND(routes, network->routes, route);
     136             : 
     137          17 :         route->network = network;
     138             : 
     139          17 :         if (ret)
     140           0 :                 *ret = route;
     141          17 :         return 0;
     142             : }
     143             : 
     144          38 : static Network *network_free(Network *network) {
     145             :         Address *address;
     146             :         Route *route;
     147             : 
     148          38 :         if (!network)
     149           0 :                 return NULL;
     150             : 
     151          38 :         free(network->ifname);
     152          38 :         free(network->hostname);
     153          38 :         strv_free(network->dns);
     154          38 :         free(network->vlan);
     155          38 :         free(network->bridge);
     156          38 :         free(network->bond);
     157             : 
     158          51 :         while ((address = network->addresses))
     159          13 :                 address_free(address);
     160             : 
     161          55 :         while ((route = network->routes))
     162          17 :                 route_free(route);
     163             : 
     164          38 :         return mfree(network);
     165             : }
     166             : 
     167          38 : DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free);
     168             : 
     169          38 : static int network_new(Context *context, const char *name, Network **ret) {
     170          38 :         _cleanup_(network_freep) Network *network = NULL;
     171          38 :         _cleanup_free_ char *ifname = NULL;
     172             :         int r;
     173             : 
     174          38 :         assert(context);
     175             : 
     176          38 :         if (!isempty(name) && !ifname_valid(name))
     177           0 :                 return -EINVAL;
     178             : 
     179          38 :         ifname = strdup(name);
     180          38 :         if (!ifname)
     181           0 :                 return -ENOMEM;
     182             : 
     183          38 :         network = new(Network, 1);
     184          38 :         if (!network)
     185           0 :                 return -ENOMEM;
     186             : 
     187          76 :         *network = (Network) {
     188          38 :                 .ifname = TAKE_PTR(ifname),
     189             :                 .dhcp_type = _DHCP_TYPE_INVALID,
     190             :                 .dhcp_use_dns = -1,
     191             :         };
     192             : 
     193          38 :         r = hashmap_ensure_allocated(&context->networks_by_name, &string_hash_ops);
     194          38 :         if (r < 0)
     195           0 :                 return r;
     196             : 
     197          38 :         r = hashmap_put(context->networks_by_name, network->ifname, network);
     198          38 :         if (r < 0)
     199           0 :                 return r;
     200             : 
     201          38 :         if (ret)
     202          38 :                 *ret = network;
     203             : 
     204          38 :         TAKE_PTR(network);
     205          38 :         return 0;
     206             : }
     207             : 
     208         133 : Network *network_get(Context *context, const char *ifname) {
     209         133 :         return hashmap_get(context->networks_by_name, ifname);
     210             : }
     211             : 
     212           7 : static NetDev *netdev_free(NetDev *netdev) {
     213           7 :         if (!netdev)
     214           0 :                 return NULL;
     215             : 
     216           7 :         free(netdev->ifname);
     217           7 :         free(netdev->kind);
     218           7 :         return mfree(netdev);
     219             : }
     220             : 
     221           7 : DEFINE_TRIVIAL_CLEANUP_FUNC(NetDev*, netdev_free);
     222             : 
     223           7 : static int netdev_new(Context *context, const char *_kind, const char *_ifname, NetDev **ret) {
     224           7 :         _cleanup_(netdev_freep) NetDev *netdev = NULL;
     225           7 :         _cleanup_free_ char *kind = NULL, *ifname = NULL;
     226             :         int r;
     227             : 
     228           7 :         assert(context);
     229             : 
     230           7 :         if (!ifname_valid(_ifname))
     231           0 :                 return -EINVAL;
     232             : 
     233           7 :         kind = strdup(_kind);
     234           7 :         if (!kind)
     235           0 :                 return -ENOMEM;
     236             : 
     237           7 :         ifname = strdup(_ifname);
     238           7 :         if (!ifname)
     239           0 :                 return -ENOMEM;
     240             : 
     241           7 :         netdev = new(NetDev, 1);
     242           7 :         if (!netdev)
     243           0 :                 return -ENOMEM;
     244             : 
     245          14 :         *netdev = (NetDev) {
     246           7 :                 .kind = TAKE_PTR(kind),
     247           7 :                 .ifname = TAKE_PTR(ifname),
     248             :         };
     249             : 
     250           7 :         r = hashmap_ensure_allocated(&context->netdevs_by_name, &string_hash_ops);
     251           7 :         if (r < 0)
     252           0 :                 return r;
     253             : 
     254           7 :         r = hashmap_put(context->netdevs_by_name, netdev->ifname, netdev);
     255           7 :         if (r < 0)
     256           0 :                 return r;
     257             : 
     258           7 :         if (ret)
     259           7 :                 *ret = netdev;
     260             : 
     261           7 :         TAKE_PTR(netdev);
     262           7 :         return 0;
     263             : }
     264             : 
     265           8 : NetDev *netdev_get(Context *context, const char *ifname) {
     266           8 :         return hashmap_get(context->netdevs_by_name, ifname);
     267             : }
     268             : 
     269           1 : static Link *link_free(Link *link) {
     270           1 :         if (!link)
     271           0 :                 return NULL;
     272             : 
     273           1 :         free(link->ifname);
     274           1 :         return mfree(link);
     275             : }
     276             : 
     277           1 : DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_free);
     278             : 
     279           1 : static int link_new(Context *context, const char *name, struct ether_addr *mac, Link **ret) {
     280           1 :         _cleanup_(link_freep) Link *link = NULL;
     281           1 :         _cleanup_free_ char *ifname = NULL;
     282             :         int r;
     283             : 
     284           1 :         assert(context);
     285             : 
     286           1 :         if (!ifname_valid(name))
     287           0 :                 return -EINVAL;
     288             : 
     289           1 :         ifname = strdup(name);
     290           1 :         if (!ifname)
     291           0 :                 return -ENOMEM;
     292             : 
     293           1 :         link = new(Link, 1);
     294           1 :         if (!link)
     295           0 :                 return -ENOMEM;
     296             : 
     297           2 :         *link = (Link) {
     298           1 :                 .ifname = TAKE_PTR(ifname),
     299           1 :                 .mac = *mac,
     300             :         };
     301             : 
     302           1 :         r = hashmap_ensure_allocated(&context->links_by_name, &string_hash_ops);
     303           1 :         if (r < 0)
     304           0 :                 return r;
     305             : 
     306           1 :         r = hashmap_put(context->links_by_name, link->ifname, link);
     307           1 :         if (r < 0)
     308           0 :                 return r;
     309             : 
     310           1 :         if (ret)
     311           0 :                 *ret = link;
     312             : 
     313           1 :         TAKE_PTR(link);
     314           1 :         return 0;
     315             : }
     316             : 
     317           1 : Link *link_get(Context *context, const char *ifname) {
     318           1 :         return hashmap_get(context->links_by_name, ifname);
     319             : }
     320             : 
     321          17 : static int network_set_dhcp_type(Context *context, const char *ifname, const char *dhcp_type) {
     322             :         Network *network;
     323             :         DHCPType t;
     324             :         int r;
     325             : 
     326          17 :         t = dracut_dhcp_type_from_string(dhcp_type);
     327          17 :         if (t < 0)
     328           0 :                 return -EINVAL;
     329             : 
     330          17 :         network = network_get(context, ifname);
     331          17 :         if (!network) {
     332          17 :                 r = network_new(context, ifname, &network);
     333          17 :                 if (r < 0)
     334           0 :                         return r;
     335             :         }
     336             : 
     337          17 :         network->dhcp_type = t;
     338          17 :         return 0;
     339             : }
     340             : 
     341          13 : static int network_set_hostname(Context *context, const char *ifname, const char *hostname) {
     342             :         Network *network;
     343             : 
     344          13 :         network = network_get(context, ifname);
     345          13 :         if (!network)
     346           0 :                 return -ENODEV;
     347             : 
     348          13 :         return free_and_strdup(&network->hostname, hostname);
     349             : }
     350             : 
     351          10 : static int network_set_mtu(Context *context, const char *ifname, int family, const char *mtu) {
     352             :         Network *network;
     353             : 
     354          10 :         network = network_get(context, ifname);
     355          10 :         if (!network)
     356           0 :                 return -ENODEV;
     357             : 
     358          10 :         return parse_mtu(family, mtu, &network->mtu);
     359             : }
     360             : 
     361           2 : static int network_set_mac_address(Context *context, const char *ifname, const char *mac) {
     362             :         Network *network;
     363             : 
     364           2 :         network = network_get(context, ifname);
     365           2 :         if (!network)
     366           0 :                 return -ENODEV;
     367             : 
     368           2 :         return ether_addr_from_string(mac, &network->mac);
     369             : }
     370             : 
     371          13 : static int network_set_address(Context *context, const char *ifname, int family, unsigned char prefixlen,
     372             :                                union in_addr_union *addr, union in_addr_union *peer) {
     373             :         Network *network;
     374             : 
     375          13 :         if (in_addr_is_null(family, addr) != 0)
     376           0 :                 return 0;
     377             : 
     378          13 :         network = network_get(context, ifname);
     379          13 :         if (!network)
     380           0 :                 return -ENODEV;
     381             : 
     382          13 :         return address_new(network, family, prefixlen, addr, peer, NULL);
     383             : }
     384             : 
     385          16 : static int network_set_route(Context *context, const char *ifname, int family, unsigned char prefixlen,
     386             :                              union in_addr_union *dest, union in_addr_union *gateway) {
     387             :         Network *network;
     388             :         int r;
     389             : 
     390          16 :         if (in_addr_is_null(family, gateway) != 0)
     391           0 :                 return 0;
     392             : 
     393          16 :         network = network_get(context, ifname);
     394          16 :         if (!network) {
     395           3 :                 r = network_new(context, ifname, &network);
     396           3 :                 if (r < 0)
     397           0 :                         return r;
     398             :         }
     399             : 
     400          16 :         return route_new(network, family, prefixlen, dest, gateway, NULL);
     401             : }
     402             : 
     403          14 : static int network_set_dns(Context *context, const char *ifname, const char *dns) {
     404             :         union in_addr_union a;
     405             :         Network *network;
     406             :         int family, r;
     407             : 
     408          14 :         r = in_addr_from_string_auto(dns, &family, &a);
     409          14 :         if (r < 0)
     410           0 :                 return r;
     411             : 
     412          14 :         network = network_get(context, ifname);
     413          14 :         if (!network) {
     414           3 :                 r = network_new(context, ifname, &network);
     415           3 :                 if (r < 0)
     416           0 :                         return r;
     417             :         }
     418             : 
     419          14 :         return strv_extend(&network->dns, dns);
     420             : }
     421             : 
     422           3 : static int network_set_dhcp_use_dns(Context *context, const char *ifname, bool value) {
     423             :         Network *network;
     424             :         int r;
     425             : 
     426           3 :         network = network_get(context, ifname);
     427           3 :         if (!network) {
     428           3 :                 r = network_new(context, ifname, &network);
     429           3 :                 if (r < 0)
     430           0 :                         return r;
     431             :         }
     432             : 
     433           3 :         network->dhcp_use_dns = value;
     434             : 
     435           3 :         return 0;
     436             : }
     437             : 
     438           1 : static int network_set_vlan(Context *context, const char *ifname, const char *value) {
     439             :         Network *network;
     440             :         int r;
     441             : 
     442           1 :         network = network_get(context, ifname);
     443           1 :         if (!network) {
     444           1 :                 r = network_new(context, ifname, &network);
     445           1 :                 if (r < 0)
     446           0 :                         return r;
     447             :         }
     448             : 
     449           1 :         return free_and_strdup(&network->vlan, value);
     450             : }
     451             : 
     452           6 : static int network_set_bridge(Context *context, const char *ifname, const char *value) {
     453             :         Network *network;
     454             :         int r;
     455             : 
     456           6 :         network = network_get(context, ifname);
     457           6 :         if (!network) {
     458           5 :                 r = network_new(context, ifname, &network);
     459           5 :                 if (r < 0)
     460           0 :                         return r;
     461             :         }
     462             : 
     463           6 :         return free_and_strdup(&network->bridge, value);
     464             : }
     465             : 
     466           6 : static int network_set_bond(Context *context, const char *ifname, const char *value) {
     467             :         Network *network;
     468             :         int r;
     469             : 
     470           6 :         network = network_get(context, ifname);
     471           6 :         if (!network) {
     472           6 :                 r = network_new(context, ifname, &network);
     473           6 :                 if (r < 0)
     474           0 :                         return r;
     475             :         }
     476             : 
     477           6 :         return free_and_strdup(&network->bond, value);
     478             : }
     479             : 
     480          10 : static int parse_cmdline_ip_mtu_mac(Context *context, const char *ifname, int family, const char *value) {
     481             :         const char *mtu, *p;
     482             :         int r;
     483             : 
     484             :         /* [<mtu>][:<macaddr>] */
     485             : 
     486          10 :         p = strchr(value, ':');
     487          10 :         if (!p)
     488           3 :                 mtu = value;
     489             :         else
     490           7 :                 mtu = strndupa(value, p - value);
     491             : 
     492          10 :         r = network_set_mtu(context, ifname, family, mtu);
     493          10 :         if (r < 0)
     494           6 :                 return r;
     495             : 
     496           4 :         if (!p)
     497           2 :                 return 0;
     498             : 
     499           2 :         r = network_set_mac_address(context, ifname, p + 1);
     500           2 :         if (r < 0)
     501           0 :                 return r;
     502             : 
     503           2 :         return 0;
     504             : }
     505             : 
     506          58 : static int parse_ip_address_one(int family, const char **value, union in_addr_union *ret) {
     507          58 :         const char *p = *value, *q, *buf;
     508             :         int r;
     509             : 
     510          58 :         if (p[0] == ':') {
     511           2 :                 *value = p + 1;
     512           2 :                 return 0;
     513             :         }
     514             : 
     515          56 :         if (family == AF_INET6) {
     516           7 :                 if (p[0] != '[')
     517           2 :                         return -EINVAL;
     518             : 
     519           5 :                 q = strchr(p + 1, ']');
     520           5 :                 if (!q)
     521           0 :                         return -EINVAL;
     522             : 
     523           5 :                 if (q[1] != ':')
     524           0 :                         return -EINVAL;
     525             : 
     526           5 :                 buf = strndupa(p + 1, q - p - 1);
     527           5 :                 p = q + 2;
     528             :         } else {
     529          49 :                 q = strchr(p, ':');
     530          49 :                 if (!q)
     531           0 :                         return -EINVAL;
     532             : 
     533          49 :                 buf = strndupa(p, q - p);
     534          49 :                 p = q + 1;
     535             :         }
     536             : 
     537          54 :         r = in_addr_from_string(family, buf, ret);
     538          54 :         if (r < 0)
     539           3 :                 return r;
     540             : 
     541          51 :         *value = p;
     542          51 :         return 1;
     543             : }
     544             : 
     545          13 : static int parse_netmask_or_prefixlen(int family, const char **value, unsigned char *ret) {
     546             :         union in_addr_union netmask;
     547             :         const char *p, *q;
     548             :         int r;
     549             : 
     550          13 :         r = parse_ip_address_one(family, value, &netmask);
     551          13 :         if (r > 0) {
     552          11 :                 if (family == AF_INET6)
     553             :                         /* TODO: Not supported yet. */
     554           0 :                         return -EINVAL;
     555             : 
     556          11 :                 *ret = in4_addr_netmask_to_prefixlen(&netmask.in);
     557           2 :         } else if (r == 0)
     558           0 :                 *ret = family == AF_INET6 ? 128 : 32;
     559             :         else {
     560           2 :                 p = strchr(*value, ':');
     561           2 :                 if (!p)
     562           0 :                         return -EINVAL;
     563             : 
     564           2 :                 q = strndupa(*value, p - *value);
     565           2 :                 r = safe_atou8(q, ret);
     566           2 :                 if (r < 0)
     567           0 :                         return r;
     568             : 
     569           2 :                 *value = p + 1;
     570             :         }
     571             : 
     572          13 :         return 0;
     573             : }
     574             : 
     575          16 : static int parse_cmdline_ip_address(Context *context, int family, const char *value) {
     576          16 :         union in_addr_union addr = {}, peer = {}, gateway = {};
     577             :         const char *hostname, *ifname, *dhcp_type, *dns, *p;
     578             :         unsigned char prefixlen;
     579             :         int r;
     580             : 
     581             :         /* ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<mtu>][:<macaddr>]]
     582             :          * ip=<client-IP>:[<peer>]:<gateway-IP>:<netmask>:<client_hostname>:<interface>:{none|off|dhcp|on|any|dhcp6|auto6|ibft}[:[<dns1>][:<dns2>]] */
     583             : 
     584          16 :         r = parse_ip_address_one(family, &value, &addr);
     585          16 :         if (r < 0)
     586           3 :                 return r;
     587          13 :         r = parse_ip_address_one(family, &value, &peer);
     588          13 :         if (r < 0)
     589           0 :                 return r;
     590          13 :         r = parse_ip_address_one(family, &value, &gateway);
     591          13 :         if (r < 0)
     592           0 :                 return r;
     593          13 :         r = parse_netmask_or_prefixlen(family, &value, &prefixlen);
     594          13 :         if (r < 0)
     595           0 :                 return r;
     596             : 
     597             :         /* hostname */
     598          13 :         p = strchr(value, ':');
     599          13 :         if (!p)
     600           0 :                 return -EINVAL;
     601             : 
     602          13 :         hostname = strndupa(value, p - value);
     603          13 :         if (!hostname_is_valid(hostname, false))
     604           0 :                 return -EINVAL;
     605             : 
     606          13 :         value = p + 1;
     607             : 
     608             :         /* ifname */
     609          13 :         p = strchr(value, ':');
     610          13 :         if (!p)
     611           0 :                 return -EINVAL;
     612             : 
     613          13 :         ifname = strndupa(value, p - value);
     614             : 
     615          13 :         value = p + 1;
     616             : 
     617             :         /* dhcp_type */
     618          13 :         p = strchr(value, ':');
     619          13 :         if (!p)
     620           5 :                 dhcp_type = value;
     621             :         else
     622           8 :                 dhcp_type = strndupa(value, p - value);
     623             : 
     624          13 :         r = network_set_dhcp_type(context, ifname, dhcp_type);
     625          13 :         if (r < 0)
     626           0 :                 return r;
     627             : 
     628             :         /* set values */
     629          13 :         r = network_set_hostname(context, ifname, hostname);
     630          13 :         if (r < 0)
     631           0 :                 return r;
     632             : 
     633          13 :         r = network_set_address(context, ifname, family, prefixlen, &addr, &peer);
     634          13 :         if (r < 0)
     635           0 :                 return r;
     636             : 
     637          13 :         r = network_set_route(context, ifname, family, 0, NULL, &gateway);
     638          13 :         if (r < 0)
     639           0 :                 return r;
     640             : 
     641          13 :         if (!p)
     642           5 :                 return 0;
     643             : 
     644             :         /* First, try [<mtu>][:<macaddr>] */
     645           8 :         r = parse_cmdline_ip_mtu_mac(context, ifname, AF_UNSPEC, p + 1);
     646           8 :         if (r >= 0)
     647           2 :                 return 0;
     648             : 
     649             :         /* Next, try [<dns1>][:<dns2>] */
     650           6 :         value = p + 1;
     651           6 :         p = strchr(value, ':');
     652           6 :         if (!p) {
     653           1 :                 r = network_set_dns(context, ifname, value);
     654           1 :                 if (r < 0)
     655           0 :                         return r;
     656             :         } else {
     657           5 :                 dns = strndupa(value, p - value);
     658           5 :                 r = network_set_dns(context, ifname, dns);
     659           5 :                 if (r < 0)
     660           0 :                         return r;
     661           5 :                 r = network_set_dns(context, ifname, p + 1);
     662           5 :                 if (r < 0)
     663           0 :                         return r;
     664             :         }
     665             : 
     666           6 :         return 0;
     667             : }
     668             : 
     669           3 : static int parse_cmdline_ip_interface(Context *context, const char *value) {
     670             :         const char *ifname, *dhcp_type, *p;
     671             :         int r;
     672             : 
     673             :         /* ip=<interface>:{dhcp|on|any|dhcp6|auto6}[:[<mtu>][:<macaddr>]] */
     674             : 
     675           3 :         p = strchr(value, ':');
     676           3 :         if (!p)
     677           0 :                 return -EINVAL;
     678             : 
     679           3 :         ifname = strndupa(value, p - value);
     680             : 
     681           3 :         value = p + 1;
     682           3 :         p = strchr(value, ':');
     683           3 :         if (!p)
     684           1 :                 dhcp_type = value;
     685             :         else
     686           2 :                 dhcp_type = strndupa(value, p - value);
     687             : 
     688           3 :         r = network_set_dhcp_type(context, ifname, dhcp_type);
     689           3 :         if (r < 0)
     690           0 :                 return r;
     691             : 
     692           3 :         if (!p)
     693           1 :                 return 0;
     694             : 
     695           2 :         return parse_cmdline_ip_mtu_mac(context, ifname, AF_UNSPEC, p + 1);
     696             : }
     697             : 
     698          17 : static int parse_cmdline_ip(Context *context, const char *key, const char *value) {
     699             :         const char *p;
     700             :         int r;
     701             : 
     702          17 :         if (proc_cmdline_value_missing(key, value))
     703           0 :                 return -EINVAL;
     704             : 
     705          17 :         p = strchr(value, ':');
     706          17 :         if (!p)
     707             :                 /* ip={dhcp|on|any|dhcp6|auto6|either6} */
     708           1 :                 return network_set_dhcp_type(context, "", value);
     709             : 
     710          16 :         if (value[0] == '[')
     711           2 :                 return parse_cmdline_ip_address(context, AF_INET6, value);
     712             : 
     713          14 :         r = parse_cmdline_ip_address(context, AF_INET, value);
     714          14 :         if (r < 0)
     715           3 :                 return parse_cmdline_ip_interface(context, value);
     716             : 
     717          11 :         return 0;
     718             : }
     719             : 
     720           3 : static int parse_cmdline_rd_route(Context *context, const char *key, const char *value) {
     721           3 :         union in_addr_union addr = {}, gateway = {};
     722             :         unsigned char prefixlen;
     723             :         const char *buf, *p;
     724             :         int family, r;
     725             : 
     726             :         /* rd.route=<net>/<netmask>:<gateway>[:<interface>] */
     727             : 
     728           3 :         if (proc_cmdline_value_missing(key, value))
     729           0 :                 return -EINVAL;
     730             : 
     731           3 :         if (value[0] == '[') {
     732           0 :                 p = strchr(value, ']');
     733           0 :                 if (!p)
     734           0 :                         return -EINVAL;
     735             : 
     736           0 :                 if (p[1] != ':')
     737           0 :                         return -EINVAL;
     738             : 
     739           0 :                 buf = strndupa(value + 1, p - value - 1);
     740           0 :                 value = p + 2;
     741           0 :                 family = AF_INET6;
     742             :         } else {
     743           3 :                 p = strchr(value, ':');
     744           3 :                 if (!p)
     745           0 :                         return -EINVAL;
     746             : 
     747           3 :                 buf = strndupa(value, p - value);
     748           3 :                 value = p + 1;
     749           3 :                 family = AF_INET;
     750             :         }
     751             : 
     752           3 :         r = in_addr_prefix_from_string(buf, family, &addr, &prefixlen);
     753           3 :         if (r < 0)
     754           0 :                 return r;
     755             : 
     756           3 :         p = strchr(value, ':');
     757           3 :         if (!p)
     758          10 :                 value = strjoina(value, ":");
     759             : 
     760           3 :         r = parse_ip_address_one(family, &value, &gateway);
     761           3 :         if (r < 0)
     762           0 :                 return r;
     763             : 
     764           3 :         return network_set_route(context, value, family, prefixlen, &addr, &gateway);
     765             : }
     766             : 
     767           3 : static int parse_cmdline_nameserver(Context *context, const char *key, const char *value) {
     768           3 :         if (proc_cmdline_value_missing(key, value))
     769           0 :                 return -EINVAL;
     770             : 
     771           3 :         return network_set_dns(context, "", value);
     772             : }
     773             : 
     774           3 : static int parse_cmdline_rd_peerdns(Context *context, const char *key, const char *value) {
     775             :         int r;
     776             : 
     777           3 :         if (proc_cmdline_value_missing(key, value))
     778           0 :                 return network_set_dhcp_use_dns(context, "", true);
     779             : 
     780           3 :         r = parse_boolean(value);
     781           3 :         if (r < 0)
     782           0 :                 return r;
     783             : 
     784           3 :         return network_set_dhcp_use_dns(context, "", r);
     785             : }
     786             : 
     787           1 : static int parse_cmdline_vlan(Context *context, const char *key, const char *value) {
     788             :         const char *name, *p;
     789             :         NetDev *netdev;
     790             :         int r;
     791             : 
     792           1 :         if (proc_cmdline_value_missing(key, value))
     793           0 :                 return -EINVAL;
     794             : 
     795           1 :         p = strchr(value, ':');
     796           1 :         if (!p)
     797           0 :                 return -EINVAL;
     798             : 
     799           1 :         name = strndupa(value, p - value);
     800             : 
     801           1 :         netdev = netdev_get(context, name);
     802           1 :         if (!netdev) {
     803           1 :                 r = netdev_new(context, "vlan", name, &netdev);
     804           1 :                 if (r < 0)
     805           0 :                         return r;
     806             :         }
     807             : 
     808           1 :         return network_set_vlan(context, p + 1, name);
     809             : }
     810             : 
     811           3 : static int parse_cmdline_bridge(Context *context, const char *key, const char *value) {
     812             :         const char *name, *p;
     813             :         NetDev *netdev;
     814             :         int r;
     815             : 
     816           3 :         if (proc_cmdline_value_missing(key, value))
     817           0 :                 return -EINVAL;
     818             : 
     819           3 :         p = strchr(value, ':');
     820           3 :         if (!p)
     821           0 :                 return -EINVAL;
     822             : 
     823           3 :         name = strndupa(value, p - value);
     824             : 
     825           3 :         netdev = netdev_get(context, name);
     826           3 :         if (!netdev) {
     827           3 :                 r = netdev_new(context, "bridge", name, &netdev);
     828           3 :                 if (r < 0)
     829           0 :                         return r;
     830             :         }
     831             : 
     832           3 :         p++;
     833           3 :         if (isempty(p))
     834           0 :                 return -EINVAL;
     835             : 
     836           6 :         for (;;) {
     837           9 :                 _cleanup_free_ char *word = NULL;
     838             : 
     839           9 :                 r = extract_first_word(&p, &word, ",", 0);
     840           9 :                 if (r == 0)
     841           3 :                         return 0;
     842           6 :                 if (r < 0)
     843           0 :                         return r;
     844             : 
     845           6 :                 r = network_set_bridge(context, word, name);
     846           6 :                 if (r < 0)
     847           0 :                         return r;
     848             :         }
     849             : }
     850             : 
     851           3 : static int parse_cmdline_bond(Context *context, const char *key, const char *value) {
     852             :         const char *name, *slaves, *p;
     853             :         NetDev *netdev;
     854             :         int r;
     855             : 
     856           3 :         if (proc_cmdline_value_missing(key, value))
     857           0 :                 return -EINVAL;
     858             : 
     859           3 :         p = strchr(value, ':');
     860           3 :         if (!p)
     861           0 :                 return -EINVAL;
     862             : 
     863           3 :         name = strndupa(value, p - value);
     864             : 
     865           3 :         netdev = netdev_get(context, name);
     866           3 :         if (!netdev) {
     867           3 :                 r = netdev_new(context, "bond", name, &netdev);
     868           3 :                 if (r < 0)
     869           0 :                         return r;
     870             :         }
     871             : 
     872           3 :         value = p + 1;
     873           3 :         p = strchr(value, ':');
     874           3 :         if (!p)
     875           1 :                 slaves = value;
     876             :         else
     877           2 :                 slaves = strndupa(value, p - value);
     878             : 
     879           3 :         if (isempty(slaves))
     880           0 :                 return -EINVAL;
     881             : 
     882           9 :         for (const char *q = slaves; ; ) {
     883           9 :                 _cleanup_free_ char *word = NULL;
     884             : 
     885           9 :                 r = extract_first_word(&q, &word, ",", 0);
     886           9 :                 if (r == 0)
     887           3 :                         break;
     888           6 :                 if (r < 0)
     889           0 :                         return r;
     890             : 
     891           6 :                 r = network_set_bond(context, word, name);
     892           6 :                 if (r < 0)
     893           0 :                         return r;
     894             :         }
     895             : 
     896           3 :         if (!p)
     897           1 :                 return 0;
     898             : 
     899           2 :         value = p + 1;
     900           2 :         p = strchr(value, ':');
     901           2 :         if (!p)
     902             :                 /* TODO: set bonding options */
     903           0 :                 return 0;
     904             : 
     905           2 :         return parse_mtu(AF_UNSPEC, p + 1, &netdev->mtu);
     906             : }
     907             : 
     908           1 : static int parse_cmdline_ifname(Context *context, const char *key, const char *value) {
     909             :         struct ether_addr mac;
     910             :         const char *name, *p;
     911             :         int r;
     912             : 
     913             :         /* ifname=<interface>:<MAC> */
     914             : 
     915           1 :         if (proc_cmdline_value_missing(key, value))
     916           0 :                 return -EINVAL;
     917             : 
     918           1 :         p = strchr(value, ':');
     919           1 :         if (!p)
     920           0 :                 return -EINVAL;
     921             : 
     922           1 :         name = strndupa(value, p - value);
     923             : 
     924           1 :         r = ether_addr_from_string(p + 1, &mac);
     925           1 :         if (r < 0)
     926           0 :                 return r;
     927             : 
     928           1 :         return link_new(context, name, &mac, NULL);
     929             : }
     930             : 
     931          34 : int parse_cmdline_item(const char *key, const char *value, void *data) {
     932          34 :         Context *context = data;
     933             : 
     934          34 :         assert(key);
     935          34 :         assert(data);
     936             : 
     937          34 :         if (streq(key, "ip"))
     938          17 :                 return parse_cmdline_ip(context, key, value);
     939          17 :         if (streq(key, "rd.route"))
     940           3 :                 return parse_cmdline_rd_route(context, key, value);
     941          14 :         if (streq(key, "nameserver"))
     942           3 :                 return parse_cmdline_nameserver(context, key, value);
     943          11 :         if (streq(key, "rd.peerdns"))
     944           3 :                 return parse_cmdline_rd_peerdns(context, key, value);
     945           8 :         if (streq(key, "vlan"))
     946           1 :                 return parse_cmdline_vlan(context, key, value);
     947           7 :         if (streq(key, "bridge"))
     948           3 :                 return parse_cmdline_bridge(context, key, value);
     949           4 :         if (streq(key, "bond"))
     950           3 :                 return parse_cmdline_bond(context, key, value);
     951           1 :         if (streq(key, "ifname"))
     952           1 :                 return parse_cmdline_ifname(context, key, value);
     953             : 
     954           0 :         return 0;
     955             : }
     956             : 
     957           5 : int context_merge_networks(Context *context) {
     958             :         Network *all, *network;
     959             :         Route *route;
     960             :         Iterator i;
     961             :         int r;
     962             : 
     963           5 :         assert(context);
     964             : 
     965             :         /* Copy settings about the following options
     966             :            rd.route=<net>/<netmask>:<gateway>[:<interface>]
     967             :            nameserver=<IP> [nameserver=<IP> ...]
     968             :            rd.peerdns=0 */
     969             : 
     970           5 :         all = network_get(context, "");
     971           5 :         if (!all)
     972           1 :                 return 0;
     973             : 
     974           4 :         if (hashmap_size(context->networks_by_name) <= 1)
     975           0 :                 return 0;
     976             : 
     977          12 :         HASHMAP_FOREACH(network, context->networks_by_name, i) {
     978           8 :                 if (network == all)
     979           4 :                         continue;
     980             : 
     981           4 :                 network->dhcp_use_dns = all->dhcp_use_dns;
     982             : 
     983           4 :                 r = strv_extend_strv(&network->dns, all->dns, false);
     984           4 :                 if (r < 0)
     985           0 :                         return r;
     986             : 
     987           5 :                 LIST_FOREACH(routes, route, all->routes) {
     988           1 :                         r = route_new(network, route->family, route->prefixlen, &route->dest, &route->gateway, NULL);
     989           1 :                         if (r < 0)
     990           0 :                                 return r;
     991             :                 }
     992             :         }
     993             : 
     994           4 :         assert_se(hashmap_remove(context->networks_by_name, "") == all);
     995           4 :         network_free(all);
     996           4 :         return 0;
     997             : }
     998             : 
     999          29 : void context_clear(Context *context) {
    1000          29 :         if (!context)
    1001           0 :                 return;
    1002             : 
    1003          63 :         hashmap_free_with_destructor(context->networks_by_name, network_free);
    1004          36 :         hashmap_free_with_destructor(context->netdevs_by_name, netdev_free);
    1005          30 :         hashmap_free_with_destructor(context->links_by_name, link_free);
    1006             : }
    1007             : 
    1008          13 : static int address_dump(Address *address, FILE *f) {
    1009          13 :         _cleanup_free_ char *addr = NULL, *peer = NULL;
    1010             :         int r;
    1011             : 
    1012          13 :         r = in_addr_prefix_to_string(address->family, &address->address, address->prefixlen, &addr);
    1013          13 :         if (r < 0)
    1014           0 :                 return r;
    1015             : 
    1016          13 :         if (in_addr_is_null(address->family, &address->peer) == 0) {
    1017          11 :                 r = in_addr_to_string(address->family, &address->peer, &peer);
    1018          11 :                 if (r < 0)
    1019           0 :                         return r;
    1020             :         }
    1021             : 
    1022          13 :         fprintf(f,
    1023             :                 "\n[Address]\n"
    1024             :                 "Address=%s\n",
    1025             :                 addr);
    1026             : 
    1027          13 :         if (peer)
    1028          11 :                 fprintf(f, "Peer=%s\n", peer);
    1029             : 
    1030          13 :         return 0;
    1031             : }
    1032             : 
    1033          16 : static int route_dump(Route *route, FILE *f) {
    1034          16 :         _cleanup_free_ char *dest = NULL, *gateway = NULL;
    1035             :         int r;
    1036             : 
    1037          16 :         if (in_addr_is_null(route->family, &route->dest) == 0) {
    1038           3 :                 r = in_addr_prefix_to_string(route->family, &route->dest, route->prefixlen, &dest);
    1039           3 :                 if (r < 0)
    1040           0 :                         return r;
    1041             :         }
    1042             : 
    1043          16 :         r = in_addr_to_string(route->family, &route->gateway, &gateway);
    1044          16 :         if (r < 0)
    1045           0 :                 return r;
    1046             : 
    1047          16 :         fputs("\n[Route]\n", f);
    1048          16 :         if (dest)
    1049           3 :                 fprintf(f, "Destination=%s\n", dest);
    1050          16 :         fprintf(f, "Gateway=%s\n", gateway);
    1051             : 
    1052          16 :         return 0;
    1053             : }
    1054             : 
    1055          27 : void network_dump(Network *network, FILE *f) {
    1056             :         char mac[ETHER_ADDR_TO_STRING_MAX];
    1057             :         Address *address;
    1058             :         Route *route;
    1059             :         const char *dhcp;
    1060             :         char **dns;
    1061             : 
    1062          27 :         assert(network);
    1063          27 :         assert(f);
    1064             : 
    1065          27 :         fprintf(f,
    1066             :                 "[Match]\n"
    1067             :                 "Name=%s\n",
    1068          27 :                 isempty(network->ifname) ? "*" : network->ifname);
    1069             : 
    1070          27 :         fputs("\n[Link]\n", f);
    1071             : 
    1072          27 :         if (!ether_addr_is_null(&network->mac))
    1073           2 :                 fprintf(f, "MACAddress=%s\n", ether_addr_to_string(&network->mac, mac));
    1074          27 :         if (network->mtu > 0)
    1075           4 :                 fprintf(f, "MTUBytes=%" PRIu32 "\n", network->mtu);
    1076             : 
    1077          27 :         fputs("\n[Network]\n", f);
    1078             : 
    1079          27 :         dhcp = networkd_dhcp_type_to_string(network->dhcp_type);
    1080          27 :         if (dhcp)
    1081          17 :                 fprintf(f, "DHCP=%s\n", dhcp);
    1082             : 
    1083          27 :         if (!strv_isempty(network->dns))
    1084          22 :                 STRV_FOREACH(dns, network->dns)
    1085          14 :                         fprintf(f, "DNS=%s\n", *dns);
    1086             : 
    1087          27 :         if (network->vlan)
    1088           1 :                 fprintf(f, "VLAN=%s\n", network->vlan);
    1089             : 
    1090          27 :         if (network->bridge)
    1091           3 :                 fprintf(f, "Bridge=%s\n", network->bridge);
    1092             : 
    1093          27 :         if (network->bond)
    1094           2 :                 fprintf(f, "Bond=%s\n", network->bond);
    1095             : 
    1096          27 :         fputs("\n[DHCP]\n", f);
    1097             : 
    1098          27 :         if (!isempty(network->hostname))
    1099          13 :                 fprintf(f, "Hostname=%s\n", network->hostname);
    1100             : 
    1101          27 :         if (network->dhcp_use_dns >= 0)
    1102           3 :                 fprintf(f, "UseDNS=%s\n", yes_no(network->dhcp_use_dns));
    1103             : 
    1104          40 :         LIST_FOREACH(addresses, address, network->addresses)
    1105          13 :                 (void) address_dump(address, f);
    1106             : 
    1107          43 :         LIST_FOREACH(routes, route, network->routes)
    1108          16 :                 (void) route_dump(route, f);
    1109          27 : }
    1110             : 
    1111           1 : void netdev_dump(NetDev *netdev, FILE *f) {
    1112           1 :         assert(netdev);
    1113           1 :         assert(f);
    1114             : 
    1115           1 :         fprintf(f,
    1116             :                 "[NetDev]\n"
    1117             :                 "Kind=%s\n"
    1118             :                 "Name=%s\n",
    1119             :                 netdev->kind,
    1120             :                 netdev->ifname);
    1121             : 
    1122           1 :         if (netdev->mtu > 0)
    1123           1 :                 fprintf(f, "MTUBytes=%" PRIu32 "\n", netdev->mtu);
    1124           1 : }
    1125             : 
    1126           1 : void link_dump(Link *link, FILE *f) {
    1127             :         char mac[ETHER_ADDR_TO_STRING_MAX];
    1128             : 
    1129           1 :         assert(link);
    1130           1 :         assert(f);
    1131             : 
    1132           1 :         fputs("[Match]\n", f);
    1133             : 
    1134           1 :         if (!ether_addr_is_null(&link->mac))
    1135           1 :                 fprintf(f, "MACAddress=%s\n", ether_addr_to_string(&link->mac, mac));
    1136             : 
    1137           1 :         fprintf(f,
    1138             :                 "\n[Link]\n"
    1139             :                 "Name=%s\n",
    1140             :                 link->ifname);
    1141           1 : }
    1142             : 
    1143          27 : int network_format(Network *network, char **ret) {
    1144          27 :         _cleanup_free_ char *s = NULL;
    1145          27 :         size_t sz = 0;
    1146             :         int r;
    1147             : 
    1148          27 :         assert(network);
    1149          27 :         assert(ret);
    1150             : 
    1151             :         {
    1152          27 :                 _cleanup_fclose_ FILE *f = NULL;
    1153             : 
    1154          27 :                 f = open_memstream_unlocked(&s, &sz);
    1155          27 :                 if (!f)
    1156           0 :                         return -ENOMEM;
    1157             : 
    1158          27 :                 network_dump(network, f);
    1159             : 
    1160             :                 /* Add terminating 0, so that the output buffer is a valid string. */
    1161          27 :                 fputc('\0', f);
    1162             : 
    1163          27 :                 r = fflush_and_check(f);
    1164             :         }
    1165          27 :         if (r < 0)
    1166           0 :                 return r;
    1167             : 
    1168          27 :         assert(s);
    1169          27 :         *ret = TAKE_PTR(s);
    1170          27 :         assert(sz > 0);
    1171          27 :         return (int) sz - 1;
    1172             : }
    1173             : 
    1174           1 : int netdev_format(NetDev *netdev, char **ret) {
    1175           1 :         _cleanup_free_ char *s = NULL;
    1176           1 :         size_t sz = 0;
    1177             :         int r;
    1178             : 
    1179           1 :         assert(netdev);
    1180           1 :         assert(ret);
    1181             : 
    1182             :         {
    1183           1 :                 _cleanup_fclose_ FILE *f = NULL;
    1184             : 
    1185           1 :                 f = open_memstream_unlocked(&s, &sz);
    1186           1 :                 if (!f)
    1187           0 :                         return -ENOMEM;
    1188             : 
    1189           1 :                 netdev_dump(netdev, f);
    1190             : 
    1191             :                 /* Add terminating 0, so that the output buffer is a valid string. */
    1192           1 :                 fputc('\0', f);
    1193             : 
    1194           1 :                 r = fflush_and_check(f);
    1195             :         }
    1196           1 :         if (r < 0)
    1197           0 :                 return r;
    1198             : 
    1199           1 :         assert(s);
    1200           1 :         *ret = TAKE_PTR(s);
    1201           1 :         assert(sz > 0);
    1202           1 :         return (int) sz - 1;
    1203             : }
    1204             : 
    1205           1 : int link_format(Link *link, char **ret) {
    1206           1 :         _cleanup_free_ char *s = NULL;
    1207           1 :         size_t sz = 0;
    1208             :         int r;
    1209             : 
    1210           1 :         assert(link);
    1211           1 :         assert(ret);
    1212             : 
    1213             :         {
    1214           1 :                 _cleanup_fclose_ FILE *f = NULL;
    1215             : 
    1216           1 :                 f = open_memstream_unlocked(&s, &sz);
    1217           1 :                 if (!f)
    1218           0 :                         return -ENOMEM;
    1219             : 
    1220           1 :                 link_dump(link, f);
    1221             : 
    1222             :                 /* Add terminating 0, so that the output buffer is a valid string. */
    1223           1 :                 fputc('\0', f);
    1224             : 
    1225           1 :                 r = fflush_and_check(f);
    1226             :         }
    1227           1 :         if (r < 0)
    1228           0 :                 return r;
    1229             : 
    1230           1 :         assert(s);
    1231           1 :         *ret = TAKE_PTR(s);
    1232           1 :         assert(sz > 0);
    1233           1 :         return (int) sz - 1;
    1234             : }

Generated by: LCOV version 1.14