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