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