chiark / gitweb /
networkd: fix minor memory leak
[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         strv_free(network->domains);
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_domains(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         char ***domains = data;
357         char **domain;
358         int r;
359
360         r = config_parse_strv(unit, filename, line, section, section_line,
361                               lvalue, ltype, rvalue, domains, userdata);
362         if (r < 0)
363                 return r;
364
365         strv_uniq(*domains);
366         network->wildcard_domain = !!strv_find(*domains, "*");
367
368         STRV_FOREACH(domain, *domains)
369                 if (is_localhost(*domain) || !hostname_is_valid(*domain) || streq(*domain, "*")) {
370                         strv_remove(*domains, *domain);
371
372                         /* We removed one entry, make sure we don't skip the next one */
373                         domain--;
374                 }
375
376         return 0;
377 }
378
379 int config_parse_tunnel(const char *unit,
380                         const char *filename,
381                         unsigned line,
382                         const char *section,
383                         unsigned section_line,
384                         const char *lvalue,
385                         int ltype,
386                         const char *rvalue,
387                         void *data,
388                         void *userdata) {
389         Network *network = userdata;
390         NetDev *netdev;
391         int r;
392
393         assert(filename);
394         assert(lvalue);
395         assert(rvalue);
396         assert(data);
397
398         r = netdev_get(network->manager, rvalue, &netdev);
399         if (r < 0) {
400                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
401                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
402                 return 0;
403         }
404
405         if (netdev->kind != NETDEV_KIND_IPIP &&
406             netdev->kind != NETDEV_KIND_SIT &&
407             netdev->kind != NETDEV_KIND_GRE &&
408             netdev->kind != NETDEV_KIND_VTI) {
409                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
410                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
411                 return 0;
412         }
413
414         r = hashmap_put(network->stacked_netdevs, netdev->ifname, netdev);
415         if (r < 0) {
416                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
417                            "Can not add VLAN '%s' to network: %s",
418                            rvalue, strerror(-r));
419                 return 0;
420         }
421
422         netdev_ref(netdev);
423
424         return 0;
425 }
426
427 static const char* const dhcp_support_table[_DHCP_SUPPORT_MAX] = {
428         [DHCP_SUPPORT_NONE] = "none",
429         [DHCP_SUPPORT_BOTH] = "both",
430         [DHCP_SUPPORT_V4] = "v4",
431         [DHCP_SUPPORT_V6] = "v6",
432 };
433
434 DEFINE_STRING_TABLE_LOOKUP(dhcp_support, DHCPSupport);
435
436 int config_parse_dhcp(
437                 const char* unit,
438                 const char *filename,
439                 unsigned line,
440                 const char *section,
441                 unsigned section_line,
442                 const char *lvalue,
443                 int ltype,
444                 const char *rvalue,
445                 void *data,
446                 void *userdata) {
447
448         DHCPSupport *dhcp = data;
449         int k;
450
451         assert(filename);
452         assert(lvalue);
453         assert(rvalue);
454         assert(data);
455
456         /* Our enum shall be a superset of booleans, hence first try
457          * to parse as boolean, and then as enum */
458
459         k = parse_boolean(rvalue);
460         if (k > 0)
461                 *dhcp = DHCP_SUPPORT_BOTH;
462         else if (k == 0)
463                 *dhcp = DHCP_SUPPORT_NONE;
464         else {
465                 DHCPSupport s;
466
467                 s = dhcp_support_from_string(rvalue);
468                 if (s < 0){
469                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse DHCP option, ignoring: %s", rvalue);
470                         return 0;
471                 }
472
473                 *dhcp = s;
474         }
475
476         return 0;
477 }
478
479 static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = {
480         [LLMNR_SUPPORT_NO] = "no",
481         [LLMNR_SUPPORT_YES] = "yes",
482         [LLMNR_SUPPORT_RESOLVE] = "resolve",
483 };
484
485 DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport);
486
487 int config_parse_llmnr(
488                 const char* unit,
489                 const char *filename,
490                 unsigned line,
491                 const char *section,
492                 unsigned section_line,
493                 const char *lvalue,
494                 int ltype,
495                 const char *rvalue,
496                 void *data,
497                 void *userdata) {
498
499         LLMNRSupport *llmnr = data;
500         int k;
501
502         assert(filename);
503         assert(lvalue);
504         assert(rvalue);
505         assert(data);
506
507         /* Our enum shall be a superset of booleans, hence first try
508          * to parse as boolean, and then as enum */
509
510         k = parse_boolean(rvalue);
511         if (k > 0)
512                 *llmnr = LLMNR_SUPPORT_YES;
513         else if (k == 0)
514                 *llmnr = LLMNR_SUPPORT_NO;
515         else {
516                 LLMNRSupport s;
517
518                 s = llmnr_support_from_string(rvalue);
519                 if (s < 0){
520                         log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue);
521                         return 0;
522                 }
523
524                 *llmnr = s;
525         }
526
527         return 0;
528 }