]> git.proxmox.com Git - systemd.git/blame - src/network/networkd-network.c
Imported Upstream version 217
[systemd.git] / src / network / networkd-network.c
CommitLineData
60f067b4
JS
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
22#include <ctype.h>
23#include <net/if.h>
24
25#include "networkd.h"
5eef597e
MP
26#include "networkd-netdev.h"
27#include "networkd-link.h"
60f067b4
JS
28#include "network-internal.h"
29#include "path-util.h"
30#include "conf-files.h"
31#include "conf-parser.h"
32#include "util.h"
33
34static int network_load_one(Manager *manager, const char *filename) {
35 _cleanup_network_free_ Network *network = NULL;
36 _cleanup_fclose_ FILE *file = NULL;
37 Route *route;
38 Address *address;
39 int r;
40
41 assert(manager);
42 assert(filename);
43
44 file = fopen(filename, "re");
45 if (!file) {
46 if (errno == ENOENT)
47 return 0;
48 else
49 return -errno;
50 }
51
5eef597e
MP
52 if (null_or_empty_fd(fileno(file))) {
53 log_debug("Skipping empty file: %s", filename);
60f067b4
JS
54 return 0;
55 }
56
57 network = new0(Network, 1);
58 if (!network)
59 return log_oom();
60
61 network->manager = manager;
62
63 LIST_HEAD_INIT(network->static_addresses);
64 LIST_HEAD_INIT(network->static_routes);
65
5eef597e
MP
66 network->stacked_netdevs = hashmap_new(&string_hash_ops);
67 if (!network->stacked_netdevs)
60f067b4
JS
68 return log_oom();
69
5eef597e 70 network->addresses_by_section = hashmap_new(NULL);
60f067b4
JS
71 if (!network->addresses_by_section)
72 return log_oom();
73
5eef597e 74 network->routes_by_section = hashmap_new(NULL);
60f067b4
JS
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
5eef597e 82 network->dhcp = DHCP_SUPPORT_NONE;
60f067b4
JS
83 network->dhcp_ntp = true;
84 network->dhcp_dns = true;
85 network->dhcp_hostname = true;
e842803a
MB
86 network->dhcp_routes = true;
87 network->dhcp_sendhost = true;
5eef597e 88 network->dhcp_route_metric = DHCP_ROUTE_METRIC;
60f067b4 89
5eef597e
MP
90 network->llmnr = LLMNR_SUPPORT_YES;
91
92 r = config_parse(NULL, filename, file,
93 "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
94 config_item_perf_lookup, network_network_gperf_lookup,
95 false, false, true, network);
96 if (r < 0)
60f067b4 97 return r;
60f067b4
JS
98
99 LIST_PREPEND(networks, manager->networks, network);
100
101 LIST_FOREACH(routes, route, network->static_routes) {
102 if (!route->family) {
103 log_warning("Route section without Gateway field configured in %s. "
104 "Ignoring", filename);
105 return 0;
106 }
107 }
108
109 LIST_FOREACH(addresses, address, network->static_addresses) {
110 if (!address->family) {
111 log_warning("Address section without Address field configured in %s. "
112 "Ignoring", filename);
113 return 0;
114 }
115 }
116
117 network = NULL;
118
119 return 0;
120}
121
122int network_load(Manager *manager) {
123 Network *network;
124 _cleanup_strv_free_ char **files = NULL;
125 char **f;
126 int r;
127
128 assert(manager);
129
130 while ((network = manager->networks))
131 network_free(network);
132
133 r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
134 if (r < 0) {
135 log_error("Failed to enumerate network files: %s", strerror(-r));
136 return r;
137 }
138
139 STRV_FOREACH_BACKWARDS(f, files) {
140 r = network_load_one(manager, *f);
141 if (r < 0)
142 return r;
143 }
144
145 return 0;
146}
147
148void network_free(Network *network) {
149 NetDev *netdev;
150 Route *route;
151 Address *address;
152 Iterator i;
153
154 if (!network)
155 return;
156
157 free(network->filename);
158
159 free(network->match_mac);
160 free(network->match_path);
161 free(network->match_driver);
162 free(network->match_type);
163 free(network->match_name);
164
165 free(network->description);
5eef597e 166 free(network->dhcp_vendor_class_identifier);
60f067b4 167
5eef597e
MP
168 strv_free(network->ntp);
169 strv_free(network->dns);
170 strv_free(network->domains);
60f067b4
JS
171
172 netdev_unref(network->bridge);
173
174 netdev_unref(network->bond);
175
5eef597e
MP
176 HASHMAP_FOREACH(netdev, network->stacked_netdevs, i) {
177 hashmap_remove(network->stacked_netdevs, netdev->ifname);
60f067b4 178 netdev_unref(netdev);
5eef597e
MP
179 }
180 hashmap_free(network->stacked_netdevs);
e842803a 181
60f067b4
JS
182 while ((route = network->static_routes))
183 route_free(route);
184
185 while ((address = network->static_addresses))
186 address_free(address);
187
188 hashmap_free(network->addresses_by_section);
189 hashmap_free(network->routes_by_section);
190
191 if (network->manager && network->manager->networks)
192 LIST_REMOVE(networks, network->manager->networks, network);
193
194 condition_free_list(network->match_host);
195 condition_free_list(network->match_virt);
196 condition_free_list(network->match_kernel);
197 condition_free_list(network->match_arch);
198
199 free(network);
200}
201
202int network_get(Manager *manager, struct udev_device *device,
203 const char *ifname, const struct ether_addr *address,
204 Network **ret) {
205 Network *network;
206
207 assert(manager);
208 assert(ret);
209
210 LIST_FOREACH(networks, network, manager->networks) {
211 if (net_match_config(network->match_mac, network->match_path,
212 network->match_driver, network->match_type,
213 network->match_name, network->match_host,
214 network->match_virt, network->match_kernel,
215 network->match_arch,
216 address,
217 udev_device_get_property_value(device, "ID_PATH"),
218 udev_device_get_driver(udev_device_get_parent(device)),
219 udev_device_get_property_value(device, "ID_NET_DRIVER"),
220 udev_device_get_devtype(device),
221 ifname)) {
e842803a 222 log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
60f067b4
JS
223 network->filename);
224 *ret = network;
225 return 0;
226 }
227 }
228
229 *ret = NULL;
230
231 return -ENOENT;
232}
233
234int network_apply(Manager *manager, Network *network, Link *link) {
235 int r;
236
237 link->network = network;
238
5eef597e
MP
239 if (network->ipv4ll_route) {
240 Route *route;
241
242 r = route_new_static(network, 0, &route);
243 if (r < 0)
244 return r;
245
246 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
247 if (r == 0)
248 return -EINVAL;
249 if (r < 0)
250 return -errno;
251
252 route->family = AF_INET;
253 route->dst_prefixlen = 16;
254 route->scope = RT_SCOPE_LINK;
255 route->metrics = IPV4LL_ROUTE_METRIC;
256 route->protocol = RTPROT_STATIC;
257 }
258
60f067b4
JS
259 if (network->dns || network->ntp) {
260 r = link_save(link);
261 if (r < 0)
262 return r;
263 }
264
265 return 0;
266}
267
268int config_parse_netdev(const char *unit,
269 const char *filename,
270 unsigned line,
271 const char *section,
272 unsigned section_line,
273 const char *lvalue,
274 int ltype,
275 const char *rvalue,
276 void *data,
277 void *userdata) {
278 Network *network = userdata;
279 _cleanup_free_ char *kind_string = NULL;
280 char *p;
281 NetDev *netdev;
282 NetDevKind kind;
283 int r;
284
285 assert(filename);
286 assert(lvalue);
287 assert(rvalue);
288 assert(data);
289
290 kind_string = strdup(lvalue);
291 if (!kind_string)
292 return log_oom();
293
294 /* the keys are CamelCase versions of the kind */
295 for (p = kind_string; *p; p++)
296 *p = tolower(*p);
297
298 kind = netdev_kind_from_string(kind_string);
299 if (kind == _NETDEV_KIND_INVALID) {
300 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
301 "Invalid NetDev kind: %s", lvalue);
302 return 0;
303 }
304
305 r = netdev_get(network->manager, rvalue, &netdev);
306 if (r < 0) {
307 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
308 "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
309 return 0;
310 }
311
312 if (netdev->kind != kind) {
313 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
314 "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
315 return 0;
316 }
317
318 switch (kind) {
319 case NETDEV_KIND_BRIDGE:
320 network->bridge = netdev;
321
322 break;
323 case NETDEV_KIND_BOND:
324 network->bond = netdev;
325
326 break;
327 case NETDEV_KIND_VLAN:
60f067b4 328 case NETDEV_KIND_MACVLAN:
e842803a 329 case NETDEV_KIND_VXLAN:
5eef597e 330 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
e842803a
MB
331 if (r < 0) {
332 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
5eef597e
MP
333 "Can not add VLAN '%s' to network: %s",
334 rvalue, strerror(-r));
e842803a
MB
335 return 0;
336 }
337
60f067b4
JS
338 break;
339 default:
340 assert_not_reached("Can not parse NetDev");
341 }
342
343 netdev_ref(netdev);
344
345 return 0;
346}
347
5eef597e
MP
348int config_parse_domains(const char *unit,
349 const char *filename,
350 unsigned line,
351 const char *section,
352 unsigned section_line,
353 const char *lvalue,
354 int ltype,
355 const char *rvalue,
356 void *data,
357 void *userdata) {
358 Network *network = userdata;
359 char ***domains = data;
360 char **domain;
361 int r;
362
363 r = config_parse_strv(unit, filename, line, section, section_line,
364 lvalue, ltype, rvalue, domains, userdata);
365 if (r < 0)
366 return r;
367
368 strv_uniq(*domains);
369 network->wildcard_domain = !!strv_find(*domains, "*");
370
371 STRV_FOREACH(domain, *domains) {
372 if (is_localhost(*domain))
373 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "'localhost' domain names may not be configured, ignoring assignment: %s", *domain);
374 else if (!hostname_is_valid(*domain)) {
375 if (!streq(*domain, "*"))
376 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "domain name is not valid, ignoring assignment: %s", *domain);
377 } else
378 continue;
379
380 strv_remove(*domains, *domain);
381
382 /* We removed one entry, make sure we don't skip the next one */
383 domain--;
384 }
385
386 return 0;
387}
388
60f067b4
JS
389int config_parse_tunnel(const char *unit,
390 const char *filename,
391 unsigned line,
392 const char *section,
393 unsigned section_line,
394 const char *lvalue,
395 int ltype,
396 const char *rvalue,
397 void *data,
398 void *userdata) {
399 Network *network = userdata;
400 NetDev *netdev;
401 int r;
402
403 assert(filename);
404 assert(lvalue);
405 assert(rvalue);
406 assert(data);
407
408 r = netdev_get(network->manager, rvalue, &netdev);
409 if (r < 0) {
410 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
411 "Tunnel is invalid, ignoring assignment: %s", rvalue);
412 return 0;
413 }
414
415 if (netdev->kind != NETDEV_KIND_IPIP &&
416 netdev->kind != NETDEV_KIND_SIT &&
417 netdev->kind != NETDEV_KIND_GRE &&
418 netdev->kind != NETDEV_KIND_VTI) {
419 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
420 "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
421 return 0;
422 }
423
5eef597e
MP
424 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
425 if (r < 0) {
426 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
427 "Can not add VLAN '%s' to network: %s",
428 rvalue, strerror(-r));
429 return 0;
430 }
431
432 netdev_ref(netdev);
433
434 return 0;
435}
436
437static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
438 [DHCP_SUPPORT_NONE] = "none",
439 [DHCP_SUPPORT_BOTH] = "both",
440 [DHCP_SUPPORT_V4] = "v4",
441 [DHCP_SUPPORT_V6] = "v6",
442};
443
444DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
445
446int config_parse_dhcp(
447 const char* unit,
448 const char *filename,
449 unsigned line,
450 const char *section,
451 unsigned section_line,
452 const char *lvalue,
453 int ltype,
454 const char *rvalue,
455 void *data,
456 void *userdata) {
457
458 DHCPSupport *dhcp = data;
459 int k;
460
461 assert(filename);
462 assert(lvalue);
463 assert(rvalue);
464 assert(data);
465
466 /* Our enum shall be a superset of booleans, hence first try
467 * to parse as boolean, and then as enum */
468
469 k = parse_boolean(rvalue);
470 if (k > 0)
471 *dhcp = DHCP_SUPPORT_BOTH;
472 else if (k == 0)
473 *dhcp = DHCP_SUPPORT_NONE;
474 else {
475 DHCPSupport s;
476
477 s = dhcp_support_from_string(rvalue);
478 if (s < 0){
479 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
480 return 0;
481 }
482
483 *dhcp = s;
484 }
485
486 return 0;
487}
488
489static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
490 [LLMNR_SUPPORT_NO] = "no",
491 [LLMNR_SUPPORT_YES] = "yes",
492 [LLMNR_SUPPORT_RESOLVE] = "resolve",
493};
494
495DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
496
497int config_parse_llmnr(
498 const char* unit,
499 const char *filename,
500 unsigned line,
501 const char *section,
502 unsigned section_line,
503 const char *lvalue,
504 int ltype,
505 const char *rvalue,
506 void *data,
507 void *userdata) {
508
509 LLMNRSupport *llmnr = data;
510 int k;
511
512 assert(filename);
513 assert(lvalue);
514 assert(rvalue);
515 assert(data);
516
517 /* Our enum shall be a superset of booleans, hence first try
518 * to parse as boolean, and then as enum */
519
520 k = parse_boolean(rvalue);
521 if (k > 0)
522 *llmnr = LLMNR_SUPPORT_YES;
523 else if (k == 0)
524 *llmnr = LLMNR_SUPPORT_NO;
525 else {
526 LLMNRSupport s;
527
528 s = llmnr_support_from_string(rvalue);
529 if (s < 0){
530 log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
531 return 0;
532 }
533
534 *llmnr = s;
535 }
60f067b4
JS
536
537 return 0;
538}