chiark / gitweb /
core: Rename Job.subscribed field to Job.clients
[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_routes = true;
87         network->dhcp_sendhost = true;
88
89         network->llmnr = LLMNR_SUPPORT_YES;
90
91         r = config_parse(NULL, filename, file,
92                          "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
93                          config_item_perf_lookup, network_network_gperf_lookup,
94                          false, false, true, network);
95         if (r < 0)
96                 return r;
97
98         LIST_PREPEND(networks, manager->networks, network);
99
100         LIST_FOREACH(routes, route, network->static_routes) {
101                 if (!route->family) {
102                         log_warning("Route section without Gateway field configured in %s. "
103                                     "Ignoring", filename);
104                         return 0;
105                 }
106         }
107
108         LIST_FOREACH(addresses, address, network->static_addresses) {
109                 if (!address->family) {
110                         log_warning("Address section without Address field configured in %s. "
111                                     "Ignoring", filename);
112                         return 0;
113                 }
114         }
115
116         network = NULL;
117
118         return 0;
119 }
120
121 int network_load(Manager *manager) {
122         Network *network;
123         _cleanup_strv_free_ char **files = NULL;
124         char **f;
125         int r;
126
127         assert(manager);
128
129         while ((network = manager->networks))
130                 network_free(network);
131
132         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
133         if (r < 0) {
134                 log_error("Failed to enumerate network files: %s", strerror(-r));
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
144         return 0;
145 }
146
147 void network_free(Network *network) {
148         NetDev *netdev;
149         Route *route;
150         Address *address;
151         Iterator i;
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);
165         free(network->dhcp_vendor_class_identifier);
166
167         strv_free(network->ntp);
168         strv_free(network->dns);
169
170         netdev_unref(network->bridge);
171
172         netdev_unref(network->bond);
173
174         HASHMAP_FOREACH(netdev, network->stacked_netdevs, i)
175                 netdev_unref(netdev);
176         hashmap_free(network->stacked_netdevs);
177
178         while ((route = network->static_routes))
179                 route_free(route);
180
181         while ((address = network->static_addresses))
182                 address_free(address);
183
184         hashmap_free(network->addresses_by_section);
185         hashmap_free(network->routes_by_section);
186
187         if (network->manager && network->manager->networks)
188                 LIST_REMOVE(networks, network->manager->networks, network);
189
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
195         free(network);
196 }
197
198 int network_get(Manager *manager, struct udev_device *device,
199                 const char *ifname, const struct ether_addr *address,
200                 Network **ret) {
201         Network *network;
202
203         assert(manager);
204         assert(ret);
205
206         LIST_FOREACH(networks, network, manager->networks) {
207                 if (net_match_config(network->match_mac, network->match_path,
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)) {
218                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
219                                   network->filename);
220                         *ret = network;
221                         return 0;
222                 }
223         }
224
225         *ret = NULL;
226
227         return -ENOENT;
228 }
229
230 int network_apply(Manager *manager, Network *network, Link *link) {
231         int r;
232
233         link->network = network;
234
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
255         if (network->dns || network->ntp) {
256                 r = link_save(link);
257                 if (r < 0)
258                         return r;
259         }
260
261         return 0;
262 }
263
264 int config_parse_netdev(const char *unit,
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;
275         _cleanup_free_ char *kind_string = NULL;
276         char *p;
277         NetDev *netdev;
278         NetDevKind kind;
279         int r;
280
281         assert(filename);
282         assert(lvalue);
283         assert(rvalue);
284         assert(data);
285
286         kind_string = strdup(lvalue);
287         if (!kind_string)
288                 return log_oom();
289
290         /* the keys are CamelCase versions of the kind */
291         for (p = kind_string; *p; p++)
292                 *p = tolower(*p);
293
294         kind = netdev_kind_from_string(kind_string);
295         if (kind == _NETDEV_KIND_INVALID) {
296                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
297                            "Invalid NetDev kind: %s", lvalue);
298                 return 0;
299         }
300
301         r = netdev_get(network->manager, rvalue, &netdev);
302         if (r < 0) {
303                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
304                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
305                 return 0;
306         }
307
308         if (netdev->kind != kind) {
309                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
310                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
311                 return 0;
312         }
313
314         switch (kind) {
315         case NETDEV_KIND_BRIDGE:
316                 network->bridge = netdev;
317
318                 break;
319         case NETDEV_KIND_BOND:
320                 network->bond = netdev;
321
322                 break;
323         case NETDEV_KIND_VLAN:
324         case NETDEV_KIND_MACVLAN:
325         case NETDEV_KIND_VXLAN:
326                 r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
327                 if (r < 0) {
328                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
329                                    "Can not add VLAN '%s' to network: %s",
330                                    rvalue, strerror(-r));
331                         return 0;
332                 }
333
334                 break;
335         default:
336                 assert_not_reached("Can not parse NetDev");
337         }
338
339         netdev_ref(netdev);
340
341         return 0;
342 }
343
344 int 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 &&
372             netdev->kind != NETDEV_KIND_GRE &&
373             netdev->kind != NETDEV_KIND_VTI) {
374                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
375                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
376                 return 0;
377         }
378
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);
388
389         return 0;
390 }
391
392 static 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
399 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
400
401 int 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
444 static 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
450 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
451
452 int 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 }