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