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