chiark / gitweb /
networkd: merge DNS and NTP entries when exporting
[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 "network-internal.h"
28 #include "path-util.h"
29 #include "conf-files.h"
30 #include "conf-parser.h"
31 #include "util.h"
32
33 static int network_load_one(Manager *manager, const char *filename) {
34         _cleanup_network_free_ Network *network = NULL;
35         _cleanup_fclose_ FILE *file = NULL;
36         Route *route;
37         Address *address;
38         int r;
39
40         assert(manager);
41         assert(filename);
42
43         file = fopen(filename, "re");
44         if (!file) {
45                 if (errno == ENOENT)
46                         return 0;
47                 else
48                         return -errno;
49         }
50
51         if (null_or_empty_fd(fileno(file))) {
52                 log_debug("Skipping empty file: %s", filename);
53                 return 0;
54         }
55
56         network = new0(Network, 1);
57         if (!network)
58                 return log_oom();
59
60         network->manager = manager;
61
62         LIST_HEAD_INIT(network->static_addresses);
63         LIST_HEAD_INIT(network->static_routes);
64
65         network->vlans = hashmap_new(string_hash_func, string_compare_func);
66         if (!network->vlans)
67                 return log_oom();
68
69         network->macvlans = hashmap_new(string_hash_func, string_compare_func);
70         if (!network->macvlans)
71                 return log_oom();
72
73         network->vxlans = hashmap_new(string_hash_func, string_compare_func);
74         if (!network->vxlans)
75                 return log_oom();
76
77         network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
78         if (!network->addresses_by_section)
79                 return log_oom();
80
81         network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
82         if (!network->routes_by_section)
83                 return log_oom();
84
85         network->filename = strdup(filename);
86         if (!network->filename)
87                 return log_oom();
88
89         network->dhcp_ntp = true;
90         network->dhcp_dns = true;
91         network->dhcp_hostname = true;
92         network->dhcp_domainname = true;
93         network->dhcp_routes = true;
94         network->dhcp_sendhost = true;
95
96         r = config_parse(NULL, filename, file,
97                          "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
98                          config_item_perf_lookup, network_network_gperf_lookup,
99                          false, false, true, network);
100         if (r < 0)
101                 return r;
102
103         LIST_PREPEND(networks, manager->networks, network);
104
105         LIST_FOREACH(routes, route, network->static_routes) {
106                 if (!route->family) {
107                         log_warning("Route section without Gateway field configured in %s. "
108                                     "Ignoring", filename);
109                         return 0;
110                 }
111         }
112
113         LIST_FOREACH(addresses, address, network->static_addresses) {
114                 if (!address->family) {
115                         log_warning("Address section without Address field configured in %s. "
116                                     "Ignoring", filename);
117                         return 0;
118                 }
119         }
120
121         network = NULL;
122
123         return 0;
124 }
125
126 int network_load(Manager *manager) {
127         Network *network;
128         _cleanup_strv_free_ char **files = NULL;
129         char **f;
130         int r;
131
132         assert(manager);
133
134         while ((network = manager->networks))
135                 network_free(network);
136
137         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
138         if (r < 0) {
139                 log_error("Failed to enumerate network files: %s", strerror(-r));
140                 return r;
141         }
142
143         STRV_FOREACH_BACKWARDS(f, files) {
144                 r = network_load_one(manager, *f);
145                 if (r < 0)
146                         return r;
147         }
148
149         return 0;
150 }
151
152 void network_free(Network *network) {
153         NetDev *netdev;
154         Route *route;
155         Address *address;
156         Iterator i;
157
158         if (!network)
159                 return;
160
161         free(network->filename);
162
163         free(network->match_mac);
164         free(network->match_path);
165         free(network->match_driver);
166         free(network->match_type);
167         free(network->match_name);
168
169         free(network->description);
170         free(network->dhcp_vendor_class_identifier);
171
172         strv_free(network->ntp);
173         strv_free(network->dns);
174
175         netdev_unref(network->bridge);
176
177         netdev_unref(network->bond);
178
179         netdev_unref(network->tunnel);
180
181         HASHMAP_FOREACH(netdev, network->vlans, i)
182                 netdev_unref(netdev);
183         hashmap_free(network->vlans);
184
185         HASHMAP_FOREACH(netdev, network->macvlans, i)
186                 netdev_unref(netdev);
187         hashmap_free(network->macvlans);
188
189         HASHMAP_FOREACH(netdev, network->vxlans, i)
190                 netdev_unref(netdev);
191         hashmap_free(network->vxlans);
192
193         while ((route = network->static_routes))
194                 route_free(route);
195
196         while ((address = network->static_addresses))
197                 address_free(address);
198
199         hashmap_free(network->addresses_by_section);
200         hashmap_free(network->routes_by_section);
201
202         if (network->manager && network->manager->networks)
203                 LIST_REMOVE(networks, network->manager->networks, network);
204
205         condition_free_list(network->match_host);
206         condition_free_list(network->match_virt);
207         condition_free_list(network->match_kernel);
208         condition_free_list(network->match_arch);
209
210         free(network);
211 }
212
213 int network_get(Manager *manager, struct udev_device *device,
214                 const char *ifname, const struct ether_addr *address,
215                 Network **ret) {
216         Network *network;
217
218         assert(manager);
219         assert(ret);
220
221         LIST_FOREACH(networks, network, manager->networks) {
222                 if (net_match_config(network->match_mac, network->match_path,
223                                      network->match_driver, network->match_type,
224                                      network->match_name, network->match_host,
225                                      network->match_virt, network->match_kernel,
226                                      network->match_arch,
227                                      address,
228                                      udev_device_get_property_value(device, "ID_PATH"),
229                                      udev_device_get_driver(udev_device_get_parent(device)),
230                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
231                                      udev_device_get_devtype(device),
232                                      ifname)) {
233                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
234                                   network->filename);
235                         *ret = network;
236                         return 0;
237                 }
238         }
239
240         *ret = NULL;
241
242         return -ENOENT;
243 }
244
245 int network_apply(Manager *manager, Network *network, Link *link) {
246         int r;
247
248         link->network = network;
249
250         if (network->dns || network->ntp) {
251                 r = link_save(link);
252                 if (r < 0)
253                         return r;
254         }
255
256         return 0;
257 }
258
259 int config_parse_netdev(const char *unit,
260                 const char *filename,
261                 unsigned line,
262                 const char *section,
263                 unsigned section_line,
264                 const char *lvalue,
265                 int ltype,
266                 const char *rvalue,
267                 void *data,
268                 void *userdata) {
269         Network *network = userdata;
270         _cleanup_free_ char *kind_string = NULL;
271         char *p;
272         NetDev *netdev;
273         NetDevKind kind;
274         int r;
275
276         assert(filename);
277         assert(lvalue);
278         assert(rvalue);
279         assert(data);
280
281         kind_string = strdup(lvalue);
282         if (!kind_string)
283                 return log_oom();
284
285         /* the keys are CamelCase versions of the kind */
286         for (p = kind_string; *p; p++)
287                 *p = tolower(*p);
288
289         kind = netdev_kind_from_string(kind_string);
290         if (kind == _NETDEV_KIND_INVALID) {
291                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
292                            "Invalid NetDev kind: %s", lvalue);
293                 return 0;
294         }
295
296         r = netdev_get(network->manager, rvalue, &netdev);
297         if (r < 0) {
298                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
299                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
300                 return 0;
301         }
302
303         if (netdev->kind != kind) {
304                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
305                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
306                 return 0;
307         }
308
309         switch (kind) {
310         case NETDEV_KIND_BRIDGE:
311                 network->bridge = netdev;
312
313                 break;
314         case NETDEV_KIND_BOND:
315                 network->bond = netdev;
316
317                 break;
318         case NETDEV_KIND_VLAN:
319                 r = hashmap_put(network->vlans, netdev->ifname, netdev);
320                 if (r < 0) {
321                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
322                                    "Can not add VLAN '%s' to network: %s",
323                                    rvalue, strerror(-r));
324                         return 0;
325                 }
326
327                 break;
328         case NETDEV_KIND_MACVLAN:
329                 r = hashmap_put(network->macvlans, netdev->ifname, netdev);
330                 if (r < 0) {
331                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
332                                    "Can not add MACVLAN '%s' to network: %s",
333                                    rvalue, strerror(-r));
334                         return 0;
335                 }
336
337                 break;
338         case NETDEV_KIND_VXLAN:
339                 r = hashmap_put(network->vxlans, netdev->ifname, netdev);
340                 if (r < 0) {
341                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
342                                    "Can not add VXLAN '%s' to network: %s",
343                                    rvalue, strerror(-r));
344                         return 0;
345                 }
346
347                 break;
348         default:
349                 assert_not_reached("Can not parse NetDev");
350         }
351
352         netdev_ref(netdev);
353
354         return 0;
355 }
356
357 int config_parse_tunnel(const char *unit,
358                         const char *filename,
359                         unsigned line,
360                         const char *section,
361                         unsigned section_line,
362                         const char *lvalue,
363                         int ltype,
364                         const char *rvalue,
365                         void *data,
366                         void *userdata) {
367         Network *network = userdata;
368         NetDev *netdev;
369         int r;
370
371         assert(filename);
372         assert(lvalue);
373         assert(rvalue);
374         assert(data);
375
376         r = netdev_get(network->manager, rvalue, &netdev);
377         if (r < 0) {
378                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
379                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
380                 return 0;
381         }
382
383         if (netdev->kind != NETDEV_KIND_IPIP &&
384             netdev->kind != NETDEV_KIND_SIT &&
385             netdev->kind != NETDEV_KIND_GRE &&
386             netdev->kind != NETDEV_KIND_VTI) {
387                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
388                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
389                 return 0;
390         }
391
392         network->tunnel = netdev;
393
394         return 0;
395 }