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