]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-network.c
kernel-install/90-loaderentry.install: fixed cmdline parsing
[thirdparty/systemd.git] / src / network / networkd-network.c
CommitLineData
f579559b
TG
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
69a93e7d 22#include <ctype.h>
987efa17 23#include <net/if.h>
69a93e7d 24
f579559b 25#include "networkd.h"
3be1d7e0 26#include "networkd-netdev.h"
0b1831c2 27#include "networkd-link.h"
c6f7c917 28#include "network-internal.h"
f579559b
TG
29#include "path-util.h"
30#include "conf-files.h"
31#include "conf-parser.h"
477e73b5 32#include "util.h"
f579559b
TG
33
34static int network_load_one(Manager *manager, const char *filename) {
35 _cleanup_network_free_ Network *network = NULL;
36 _cleanup_fclose_ FILE *file = NULL;
b3070dc0
TG
37 Route *route;
38 Address *address;
f579559b
TG
39 int r;
40
bf1bc670
TA
41 assert(manager);
42 assert(filename);
43
f579559b
TG
44 file = fopen(filename, "re");
45 if (!file) {
46 if (errno == ENOENT)
47 return 0;
48 else
ecb08ec6 49 return -errno;
f579559b
TG
50 }
51
ed88bcfb
ZJS
52 if (null_or_empty_fd(fileno(file))) {
53 log_debug("Skipping empty file: %s", filename);
6916ec29
TG
54 return 0;
55 }
56
f579559b
TG
57 network = new0(Network, 1);
58 if (!network)
59 return log_oom();
60
5a3eb5a7
TG
61 network->manager = manager;
62
f048a16b
TG
63 LIST_HEAD_INIT(network->static_addresses);
64 LIST_HEAD_INIT(network->static_routes);
f579559b 65
6a0a2f86
TG
66 network->stacked_netdevs = hashmap_new(string_hash_func, string_compare_func);
67 if (!network->stacked_netdevs)
326cb406
SS
68 return log_oom();
69
16aa63a0 70 network->addresses_by_section = hashmap_new(NULL, NULL);
6ae115c1
TG
71 if (!network->addresses_by_section)
72 return log_oom();
73
16aa63a0 74 network->routes_by_section = hashmap_new(NULL, NULL);
6ae115c1
TG
75 if (!network->routes_by_section)
76 return log_oom();
77
78 network->filename = strdup(filename);
79 if (!network->filename)
80 return log_oom();
81
bd8f6538 82 network->dhcp = DHCP_SUPPORT_NONE;
bcb7a07e 83 network->dhcp_ntp = true;
5be4d38e 84 network->dhcp_dns = true;
1346b1f0 85 network->dhcp_hostname = true;
e1ea665e 86 network->dhcp_routes = true;
4cc7a82c 87 network->dhcp_sendhost = true;
5be4d38e 88
bd8f6538
TG
89 network->llmnr = LLMNR_SUPPORT_YES;
90
e9f3d2d5
ZJS
91 r = config_parse(NULL, filename, file,
92 "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
93 config_item_perf_lookup, network_network_gperf_lookup,
36f822c4
ZJS
94 false, false, true, network);
95 if (r < 0)
f579559b 96 return r;
f579559b 97
f579559b 98 LIST_PREPEND(networks, manager->networks, network);
b3070dc0 99
3d3d4255 100 LIST_FOREACH(routes, route, network->static_routes) {
b3070dc0
TG
101 if (!route->family) {
102 log_warning("Route section without Gateway field configured in %s. "
103 "Ignoring", filename);
104 return 0;
105 }
b3070dc0
TG
106 }
107
3d3d4255 108 LIST_FOREACH(addresses, address, network->static_addresses) {
b3070dc0
TG
109 if (!address->family) {
110 log_warning("Address section without Address field configured in %s. "
111 "Ignoring", filename);
112 return 0;
113 }
114 }
115
f579559b
TG
116 network = NULL;
117
118 return 0;
119}
120
121int network_load(Manager *manager) {
122 Network *network;
477e73b5
ZJS
123 _cleanup_strv_free_ char **files = NULL;
124 char **f;
f579559b
TG
125 int r;
126
127 assert(manager);
128
129 while ((network = manager->networks))
130 network_free(network);
131
2ad8416d 132 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
f579559b 133 if (r < 0) {
449f7554 134 log_error("Failed to enumerate network files: %s", strerror(-r));
f579559b
TG
135 return r;
136 }
137
138 STRV_FOREACH_BACKWARDS(f, files) {
139 r = network_load_one(manager, *f);
140 if (r < 0)
141 return r;
142 }
143
f579559b
TG
144 return 0;
145}
146
f579559b 147void network_free(Network *network) {
47e2dc31 148 NetDev *netdev;
f579559b
TG
149 Route *route;
150 Address *address;
06f021a8 151 Iterator i;
f579559b
TG
152
153 if (!network)
154 return;
155
156 free(network->filename);
157
158 free(network->match_mac);
159 free(network->match_path);
160 free(network->match_driver);
161 free(network->match_type);
162 free(network->match_name);
163
164 free(network->description);
edb85f0d 165 free(network->dhcp_vendor_class_identifier);
f579559b 166
b0e39c82
TG
167 strv_free(network->ntp);
168 strv_free(network->dns);
3bef724f 169
47e2dc31
TG
170 netdev_unref(network->bridge);
171
172 netdev_unref(network->bond);
173
6a0a2f86 174 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i)
326cb406 175 netdev_unref(netdev);
6a0a2f86 176 hashmap_free(network->stacked_netdevs);
326cb406 177
f048a16b 178 while ((route = network->static_routes))
f579559b
TG
179 route_free(route);
180
f048a16b 181 while ((address = network->static_addresses))
f579559b
TG
182 address_free(address);
183
6ae115c1
TG
184 hashmap_free(network->addresses_by_section);
185 hashmap_free(network->routes_by_section);
186
b3070dc0 187 if (network->manager && network->manager->networks)
7384fa92 188 LIST_REMOVE(networks, network->manager->networks, network);
f579559b 189
79e16ce3
LP
190 condition_free_list(network->match_host);
191 condition_free_list(network->match_virt);
192 condition_free_list(network->match_kernel);
193 condition_free_list(network->match_arch);
194
f579559b
TG
195 free(network);
196}
197
505f8da7
TG
198int network_get(Manager *manager, struct udev_device *device,
199 const char *ifname, const struct ether_addr *address,
200 Network **ret) {
f579559b
TG
201 Network *network;
202
203 assert(manager);
f579559b
TG
204 assert(ret);
205
f579559b
TG
206 LIST_FOREACH(networks, network, manager->networks) {
207 if (net_match_config(network->match_mac, network->match_path,
505f8da7
TG
208 network->match_driver, network->match_type,
209 network->match_name, network->match_host,
210 network->match_virt, network->match_kernel,
211 network->match_arch,
212 address,
213 udev_device_get_property_value(device, "ID_PATH"),
214 udev_device_get_driver(udev_device_get_parent(device)),
215 udev_device_get_property_value(device, "ID_NET_DRIVER"),
216 udev_device_get_devtype(device),
217 ifname)) {
97578344 218 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
505f8da7 219 network->filename);
f579559b
TG
220 *ret = network;
221 return 0;
222 }
223 }
224
225 *ret = NULL;
226
227 return -ENOENT;
228}
229
230int network_apply(Manager *manager, Network *network, Link *link) {
f579559b
TG
231 int r;
232
f579559b
TG
233 link->network = network;
234
bfa695b5
TG
235 if (network->ipv4ll_route) {
236 Route *route;
237
238 r = route_new_static(network, 0, &route);
239 if (r < 0)
240 return r;
241
242 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
243 if (r == 0)
244 return -EINVAL;
245 if (r < 0)
246 return -errno;
247
248 route->family = AF_INET;
249 route->dst_prefixlen = 16;
250 route->scope = RT_SCOPE_LINK;
251 route->metrics = IPV4LL_ROUTE_METRIC;
252 route->protocol = RTPROT_STATIC;
253 }
254
bcb7a07e 255 if (network->dns || network->ntp) {
091a364c 256 r = link_save(link);
3bef724f
TG
257 if (r < 0)
258 return r;
259 }
260
f579559b
TG
261 return 0;
262}
02b59d57 263
69a93e7d 264int config_parse_netdev(const char *unit,
02b59d57
TG
265 const char *filename,
266 unsigned line,
267 const char *section,
268 unsigned section_line,
269 const char *lvalue,
270 int ltype,
271 const char *rvalue,
272 void *data,
273 void *userdata) {
274 Network *network = userdata;
31d0ac36
TG
275 _cleanup_free_ char *kind_string = NULL;
276 char *p;
1a436809 277 NetDev *netdev;
69a93e7d 278 NetDevKind kind;
02b59d57
TG
279 int r;
280
281 assert(filename);
282 assert(lvalue);
283 assert(rvalue);
284 assert(data);
285
69a93e7d
TG
286 kind_string = strdup(lvalue);
287 if (!kind_string)
288 return log_oom();
52433f6b 289
69a93e7d
TG
290 /* the keys are CamelCase versions of the kind */
291 for (p = kind_string; *p; p++)
292 *p = tolower(*p);
52433f6b 293
69a93e7d
TG
294 kind = netdev_kind_from_string(kind_string);
295 if (kind == _NETDEV_KIND_INVALID) {
52433f6b 296 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 297 "Invalid NetDev kind: %s", lvalue);
52433f6b
TG
298 return 0;
299 }
300
54abf461
TG
301 r = netdev_get(network->manager, rvalue, &netdev);
302 if (r < 0) {
303 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 304 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
305 return 0;
306 }
307
69a93e7d 308 if (netdev->kind != kind) {
54abf461 309 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
69a93e7d 310 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
54abf461
TG
311 return 0;
312 }
313
69a93e7d
TG
314 switch (kind) {
315 case NETDEV_KIND_BRIDGE:
316 network->bridge = netdev;
54abf461 317
69a93e7d
TG
318 break;
319 case NETDEV_KIND_BOND:
320 network->bond = netdev;
fe6b2d55 321
69a93e7d
TG
322 break;
323 case NETDEV_KIND_VLAN:
69a93e7d 324 case NETDEV_KIND_MACVLAN:
326cb406 325 case NETDEV_KIND_VXLAN:
6a0a2f86 326 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
326cb406
SS
327 if (r < 0) {
328 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
6a0a2f86 329 "Can not add VLAN '%s' to network: %s",
aa9f1140 330 rvalue, strerror(-r));
326cb406
SS
331 return 0;
332 }
333
69a93e7d
TG
334 break;
335 default:
336 assert_not_reached("Can not parse NetDev");
fe6b2d55
TG
337 }
338
47e2dc31
TG
339 netdev_ref(netdev);
340
fe6b2d55
TG
341 return 0;
342}
7951dea2
SS
343
344int config_parse_tunnel(const char *unit,
345 const char *filename,
346 unsigned line,
347 const char *section,
348 unsigned section_line,
349 const char *lvalue,
350 int ltype,
351 const char *rvalue,
352 void *data,
353 void *userdata) {
354 Network *network = userdata;
355 NetDev *netdev;
356 int r;
357
358 assert(filename);
359 assert(lvalue);
360 assert(rvalue);
361 assert(data);
362
363 r = netdev_get(network->manager, rvalue, &netdev);
364 if (r < 0) {
365 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
366 "Tunnel is invalid, ignoring assignment: %s", rvalue);
367 return 0;
368 }
369
370 if (netdev->kind != NETDEV_KIND_IPIP &&
371 netdev->kind != NETDEV_KIND_SIT &&
a613382b
SS
372 netdev->kind != NETDEV_KIND_GRE &&
373 netdev->kind != NETDEV_KIND_VTI) {
7951dea2
SS
374 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
375 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
376 return 0;
377 }
378
6a0a2f86
TG
379 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
380 if (r < 0) {
381 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
382 "Can not add VLAN '%s' to network: %s",
383 rvalue, strerror(-r));
384 return 0;
385 }
386
387 netdev_ref(netdev);
7951dea2
SS
388
389 return 0;
390}
bd8f6538
TG
391
392static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
393 [DHCP_SUPPORT_NONE] = "none",
394 [DHCP_SUPPORT_BOTH] = "both",
395 [DHCP_SUPPORT_V4] = "v4",
396 [DHCP_SUPPORT_V6] = "v6",
397};
398
399DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
400
401int config_parse_dhcp(
402 const char* unit,
403 const char *filename,
404 unsigned line,
405 const char *section,
406 unsigned section_line,
407 const char *lvalue,
408 int ltype,
409 const char *rvalue,
410 void *data,
411 void *userdata) {
412
413 DHCPSupport *dhcp = data;
414 int k;
415
416 assert(filename);
417 assert(lvalue);
418 assert(rvalue);
419 assert(data);
420
421 /* Our enum shall be a superset of booleans, hence first try
422 * to parse as boolean, and then as enum */
423
424 k = parse_boolean(rvalue);
425 if (k > 0)
426 *dhcp = DHCP_SUPPORT_BOTH;
427 else if (k == 0)
428 *dhcp = DHCP_SUPPORT_NONE;
429 else {
430 DHCPSupport s;
431
432 s = dhcp_support_from_string(rvalue);
433 if (s < 0){
434 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
435 return 0;
436 }
437
438 *dhcp = s;
439 }
440
441 return 0;
442}
443
444static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
445 [LLMNR_SUPPORT_NO] = "no",
446 [LLMNR_SUPPORT_YES] = "yes",
447 [LLMNR_SUPPORT_RESOLVE] = "resolve",
448};
449
450DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
451
452int config_parse_llmnr(
453 const char* unit,
454 const char *filename,
455 unsigned line,
456 const char *section,
457 unsigned section_line,
458 const char *lvalue,
459 int ltype,
460 const char *rvalue,
461 void *data,
462 void *userdata) {
463
464 LLMNRSupport *llmnr = data;
465 int k;
466
467 assert(filename);
468 assert(lvalue);
469 assert(rvalue);
470 assert(data);
471
472 /* Our enum shall be a superset of booleans, hence first try
473 * to parse as boolean, and then as enum */
474
475 k = parse_boolean(rvalue);
476 if (k > 0)
477 *llmnr = LLMNR_SUPPORT_YES;
478 else if (k == 0)
479 *llmnr = LLMNR_SUPPORT_NO;
480 else {
481 LLMNRSupport s;
482
483 s = llmnr_support_from_string(rvalue);
484 if (s < 0){
485 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
486 return 0;
487 }
488
489 *llmnr = s;
490 }
491
492 return 0;
493}