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