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