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