chiark / gitweb /
networkctl: name setup state variable setup_state
[elogind.git] / src / network / networkd-network.c
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"
26 #include "networkd-netdev.h"
27 #include "networkd-link.h"
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
34 static 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
52         if (null_or_empty_fd(fileno(file))) {
53                 log_debug("Skipping empty file: %s", filename);
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
66         network->stacked_netdevs = hashmap_new(string_hash_func, string_compare_func);
67         if (!network->stacked_netdevs)
68                 return log_oom();
69
70         network->addresses_by_section = hashmap_new(NULL, NULL);
71         if (!network->addresses_by_section)
72                 return log_oom();
73
74         network->routes_by_section = hashmap_new(NULL, NULL);
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
82         network->dhcp = DHCP_SUPPORT_NONE;
83         network->dhcp_ntp = true;
84         network->dhcp_dns = true;
85         network->dhcp_hostname = true;
86         network->dhcp_domainname = true;
87         network->dhcp_routes = true;
88         network->dhcp_sendhost = true;
89
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)
97                 return r;
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
122 int 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
148 void 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);
166         free(network->dhcp_vendor_class_identifier);
167
168         strv_free(network->ntp);
169         strv_free(network->dns);
170
171         netdev_unref(network->bridge);
172
173         netdev_unref(network->bond);
174
175         HASHMAP_FOREACH(netdev, network->stacked_netdevs, i)
176                 netdev_unref(netdev);
177         hashmap_free(network->stacked_netdevs);
178
179         while ((route = network->static_routes))
180                 route_free(route);
181
182         while ((address = network->static_addresses))
183                 address_free(address);
184
185         hashmap_free(network->addresses_by_section);
186         hashmap_free(network->routes_by_section);
187
188         if (network->manager && network->manager->networks)
189                 LIST_REMOVE(networks, network->manager->networks, network);
190
191         condition_free_list(network->match_host);
192         condition_free_list(network->match_virt);
193         condition_free_list(network->match_kernel);
194         condition_free_list(network->match_arch);
195
196         free(network);
197 }
198
199 int network_get(Manager *manager, struct udev_device *device,
200                 const char *ifname, const struct ether_addr *address,
201                 Network **ret) {
202         Network *network;
203
204         assert(manager);
205         assert(ret);
206
207         LIST_FOREACH(networks, network, manager->networks) {
208                 if (net_match_config(network->match_mac, network->match_path,
209                                      network->match_driver, network->match_type,
210                                      network->match_name, network->match_host,
211                                      network->match_virt, network->match_kernel,
212                                      network->match_arch,
213                                      address,
214                                      udev_device_get_property_value(device, "ID_PATH"),
215                                      udev_device_get_driver(udev_device_get_parent(device)),
216                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
217                                      udev_device_get_devtype(device),
218                                      ifname)) {
219                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
220                                   network->filename);
221                         *ret = network;
222                         return 0;
223                 }
224         }
225
226         *ret = NULL;
227
228         return -ENOENT;
229 }
230
231 int network_apply(Manager *manager, Network *network, Link *link) {
232         int r;
233
234         link->network = network;
235
236         if (network->ipv4ll_route) {
237                 Route *route;
238
239                 r = route_new_static(network, 0, &route);
240                 if (r < 0)
241                         return r;
242
243                 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
244                 if (r == 0)
245                         return -EINVAL;
246                 if (r < 0)
247                         return -errno;
248
249                 route->family = AF_INET;
250                 route->dst_prefixlen = 16;
251                 route->scope = RT_SCOPE_LINK;
252                 route->metrics = IPV4LL_ROUTE_METRIC;
253                 route->protocol = RTPROT_STATIC;
254         }
255
256         if (network->dns || network->ntp) {
257                 r = link_save(link);
258                 if (r < 0)
259                         return r;
260         }
261
262         return 0;
263 }
264
265 int config_parse_netdev(const char *unit,
266                 const char *filename,
267                 unsigned line,
268                 const char *section,
269                 unsigned section_line,
270                 const char *lvalue,
271                 int ltype,
272                 const char *rvalue,
273                 void *data,
274                 void *userdata) {
275         Network *network = userdata;
276         _cleanup_free_ char *kind_string = NULL;
277         char *p;
278         NetDev *netdev;
279         NetDevKind kind;
280         int r;
281
282         assert(filename);
283         assert(lvalue);
284         assert(rvalue);
285         assert(data);
286
287         kind_string = strdup(lvalue);
288         if (!kind_string)
289                 return log_oom();
290
291         /* the keys are CamelCase versions of the kind */
292         for (p = kind_string; *p; p++)
293                 *p = tolower(*p);
294
295         kind = netdev_kind_from_string(kind_string);
296         if (kind == _NETDEV_KIND_INVALID) {
297                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
298                            "Invalid NetDev kind: %s", lvalue);
299                 return 0;
300         }
301
302         r = netdev_get(network->manager, rvalue, &netdev);
303         if (r < 0) {
304                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
305                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
306                 return 0;
307         }
308
309         if (netdev->kind != kind) {
310                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
311                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
312                 return 0;
313         }
314
315         switch (kind) {
316         case NETDEV_KIND_BRIDGE:
317                 network->bridge = netdev;
318
319                 break;
320         case NETDEV_KIND_BOND:
321                 network->bond = netdev;
322
323                 break;
324         case NETDEV_KIND_VLAN:
325         case NETDEV_KIND_MACVLAN:
326         case NETDEV_KIND_VXLAN:
327                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
328                 if (r < 0) {
329                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
330                                    "Can not add VLAN '%s' to network: %s",
331                                    rvalue, strerror(-r));
332                         return 0;
333                 }
334
335                 break;
336         default:
337                 assert_not_reached("Can not parse NetDev");
338         }
339
340         netdev_ref(netdev);
341
342         return 0;
343 }
344
345 int config_parse_tunnel(const char *unit,
346                         const char *filename,
347                         unsigned line,
348                         const char *section,
349                         unsigned section_line,
350                         const char *lvalue,
351                         int ltype,
352                         const char *rvalue,
353                         void *data,
354                         void *userdata) {
355         Network *network = userdata;
356         NetDev *netdev;
357         int r;
358
359         assert(filename);
360         assert(lvalue);
361         assert(rvalue);
362         assert(data);
363
364         r = netdev_get(network->manager, rvalue, &netdev);
365         if (r < 0) {
366                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
367                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
368                 return 0;
369         }
370
371         if (netdev->kind != NETDEV_KIND_IPIP &&
372             netdev->kind != NETDEV_KIND_SIT &&
373             netdev->kind != NETDEV_KIND_GRE &&
374             netdev->kind != NETDEV_KIND_VTI) {
375                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
376                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
377                 return 0;
378         }
379
380         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
381         if (r < 0) {
382                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
383                            "Can not add VLAN '%s' to network: %s",
384                            rvalue, strerror(-r));
385                 return 0;
386         }
387
388         netdev_ref(netdev);
389
390         return 0;
391 }
392
393 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
394         [DHCP_SUPPORT_NONE] = "none",
395         [DHCP_SUPPORT_BOTH] = "both",
396         [DHCP_SUPPORT_V4] = "v4",
397         [DHCP_SUPPORT_V6] = "v6",
398 };
399
400 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
401
402 int config_parse_dhcp(
403                 const char* unit,
404                 const char *filename,
405                 unsigned line,
406                 const char *section,
407                 unsigned section_line,
408                 const char *lvalue,
409                 int ltype,
410                 const char *rvalue,
411                 void *data,
412                 void *userdata) {
413
414         DHCPSupport *dhcp = data;
415         int k;
416
417         assert(filename);
418         assert(lvalue);
419         assert(rvalue);
420         assert(data);
421
422         /* Our enum shall be a superset of booleans, hence first try
423          * to parse as boolean, and then as enum */
424
425         k = parse_boolean(rvalue);
426         if (k > 0)
427                 *dhcp = DHCP_SUPPORT_BOTH;
428         else if (k == 0)
429                 *dhcp = DHCP_SUPPORT_NONE;
430         else {
431                 DHCPSupport s;
432
433                 s = dhcp_support_from_string(rvalue);
434                 if (s < 0){
435                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
436                         return 0;
437                 }
438
439                 *dhcp = s;
440         }
441
442         return 0;
443 }
444
445 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
446         [LLMNR_SUPPORT_NO] = "no",
447         [LLMNR_SUPPORT_YES] = "yes",
448         [LLMNR_SUPPORT_RESOLVE] = "resolve",
449 };
450
451 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
452
453 int config_parse_llmnr(
454                 const char* unit,
455                 const char *filename,
456                 unsigned line,
457                 const char *section,
458                 unsigned section_line,
459                 const char *lvalue,
460                 int ltype,
461                 const char *rvalue,
462                 void *data,
463                 void *userdata) {
464
465         LLMNRSupport *llmnr = data;
466         int k;
467
468         assert(filename);
469         assert(lvalue);
470         assert(rvalue);
471         assert(data);
472
473         /* Our enum shall be a superset of booleans, hence first try
474          * to parse as boolean, and then as enum */
475
476         k = parse_boolean(rvalue);
477         if (k > 0)
478                 *llmnr = LLMNR_SUPPORT_YES;
479         else if (k == 0)
480                 *llmnr = LLMNR_SUPPORT_NO;
481         else {
482                 LLMNRSupport s;
483
484                 s = llmnr_support_from_string(rvalue);
485                 if (s < 0){
486                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
487                         return 0;
488                 }
489
490                 *llmnr = s;
491         }
492
493         return 0;
494 }