chiark / gitweb /
networkd: allow more than one static DNS server
[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 "networkd.h"
23 #include "net-util.h"
24 #include "path-util.h"
25 #include "conf-files.h"
26 #include "conf-parser.h"
27 #include "util.h"
28
29 static int network_load_one(Manager *manager, const char *filename) {
30         _cleanup_network_free_ Network *network = NULL;
31         _cleanup_fclose_ FILE *file = NULL;
32         Route *route;
33         Address *address;
34         int r;
35
36         assert(manager);
37         assert(filename);
38
39         file = fopen(filename, "re");
40         if (!file) {
41                 if (errno == ENOENT)
42                         return 0;
43                 else
44                         return errno;
45         }
46
47         network = new0(Network, 1);
48         if (!network)
49                 return log_oom();
50
51         network->manager = manager;
52
53         LIST_HEAD_INIT(network->static_addresses);
54         LIST_HEAD_INIT(network->static_routes);
55
56         network->vlans = hashmap_new(string_hash_func, string_compare_func);
57         if (!network->vlans)
58                 return log_oom();
59
60         network->macvlans = hashmap_new(uint64_hash_func, uint64_compare_func);
61         if (!network->macvlans)
62                 return log_oom();
63
64         network->addresses_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
65         if (!network->addresses_by_section)
66                 return log_oom();
67
68         network->routes_by_section = hashmap_new(uint64_hash_func, uint64_compare_func);
69         if (!network->routes_by_section)
70                 return log_oom();
71
72         network->dns = set_new(NULL, NULL);
73         if (!network->dns)
74                 return log_oom();
75
76         network->filename = strdup(filename);
77         if (!network->filename)
78                 return log_oom();
79
80         network->dhcp_dns = true;
81         network->dhcp_hostname = true;
82         network->dhcp_domainname = true;
83
84         r = config_parse(NULL, filename, file, "Match\0Network\0Address\0Route\0DHCPv4\0", config_item_perf_lookup,
85                         (void*) network_network_gperf_lookup, false, false, network);
86         if (r < 0) {
87                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
88                 return r;
89         }
90
91         LIST_PREPEND(networks, manager->networks, network);
92
93         LIST_FOREACH(static_routes, route, network->static_routes) {
94                 if (!route->family) {
95                         log_warning("Route section without Gateway field configured in %s. "
96                                     "Ignoring", filename);
97                         return 0;
98                 }
99         }
100
101         LIST_FOREACH(static_addresses, address, network->static_addresses) {
102                 if (!address->family) {
103                         log_warning("Address section without Address field configured in %s. "
104                                     "Ignoring", filename);
105                         return 0;
106                 }
107         }
108
109         network = NULL;
110
111         return 0;
112 }
113
114 int network_load(Manager *manager) {
115         Network *network;
116         _cleanup_strv_free_ char **files = NULL;
117         char **f;
118         int r;
119
120         assert(manager);
121
122         while ((network = manager->networks))
123                 network_free(network);
124
125         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
126         if (r < 0) {
127                 log_error("Failed to enumerate network files: %s", strerror(-r));
128                 return r;
129         }
130
131         STRV_FOREACH_BACKWARDS(f, files) {
132                 r = network_load_one(manager, *f);
133                 if (r < 0)
134                         return r;
135         }
136
137         return 0;
138 }
139
140 void network_free(Network *network) {
141         Route *route;
142         Address *address;
143         Iterator i;
144
145         if (!network)
146                 return;
147
148         free(network->filename);
149
150         free(network->match_mac);
151         free(network->match_path);
152         free(network->match_driver);
153         free(network->match_type);
154         free(network->match_name);
155
156         free(network->description);
157
158         SET_FOREACH(address, network->dns, i)
159                 address_free(address);
160
161         set_free(network->dns);
162
163         hashmap_free(network->vlans);
164
165         hashmap_free(network->macvlans);
166
167         while ((route = network->static_routes))
168                 route_free(route);
169
170         while ((address = network->static_addresses))
171                 address_free(address);
172
173         hashmap_free(network->addresses_by_section);
174         hashmap_free(network->routes_by_section);
175
176         if (network->manager && network->manager->networks)
177                 LIST_REMOVE(networks, network->manager->networks, network);
178
179         free(network);
180 }
181
182 int network_get(Manager *manager, struct udev_device *device, Network **ret) {
183         Network *network;
184
185         assert(manager);
186         assert(device);
187         assert(ret);
188
189         LIST_FOREACH(networks, network, manager->networks) {
190                 if (net_match_config(network->match_mac, network->match_path,
191                                         network->match_driver, network->match_type,
192                                         network->match_name, network->match_host,
193                                         network->match_virt, network->match_kernel,
194                                         network->match_arch,
195                                         udev_device_get_sysattr_value(device, "address"),
196                                         udev_device_get_property_value(device, "ID_PATH"),
197                                         udev_device_get_driver(udev_device_get_parent(device)),
198                                         udev_device_get_property_value(device, "ID_NET_DRIVER"),
199                                         udev_device_get_devtype(device),
200                                         udev_device_get_sysname(device))) {
201                         log_debug("%s: found matching network '%s'",
202                                         udev_device_get_sysname(device),
203                                         network->filename);
204                         *ret = network;
205                         return 0;
206                 }
207         }
208
209         *ret = NULL;
210
211         return -ENOENT;
212 }
213
214 int network_apply(Manager *manager, Network *network, Link *link) {
215         int r;
216
217         link->network = network;
218
219         if (network->dns) {
220                 r = manager_update_resolv_conf(manager);
221                 if (r < 0)
222                         return r;
223         }
224
225         return 0;
226 }
227
228 int config_parse_bridge(const char *unit,
229                 const char *filename,
230                 unsigned line,
231                 const char *section,
232                 unsigned section_line,
233                 const char *lvalue,
234                 int ltype,
235                 const char *rvalue,
236                 void *data,
237                 void *userdata) {
238         Network *network = userdata;
239         NetDev *netdev;
240         int r;
241
242         assert(filename);
243         assert(lvalue);
244         assert(rvalue);
245         assert(data);
246
247         r = netdev_get(network->manager, rvalue, &netdev);
248         if (r < 0) {
249                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
250                            "Bridge is invalid, ignoring assignment: %s", rvalue);
251                 return 0;
252         }
253
254         if (netdev->kind != NETDEV_KIND_BRIDGE) {
255                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
256                            "NetDev is not a bridge, ignoring assignment: %s", rvalue);
257                 return 0;
258         }
259
260         network->bridge = netdev;
261
262         return 0;
263 }
264
265 int config_parse_bond(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         NetDev *netdev;
277         int r;
278
279         assert(filename);
280         assert(lvalue);
281         assert(rvalue);
282         assert(data);
283
284         r = netdev_get(network->manager, rvalue, &netdev);
285         if (r < 0) {
286                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
287                            "Bond is invalid, ignoring assignment: %s", rvalue);
288                 return 0;
289         }
290
291         if (netdev->kind != NETDEV_KIND_BOND) {
292                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
293                            "NetDev is not a bond, ignoring assignment: %s", rvalue);
294                 return 0;
295         }
296
297         network->bond = netdev;
298
299         return 0;
300 }
301
302 int config_parse_vlan(const char *unit,
303                 const char *filename,
304                 unsigned line,
305                 const char *section,
306                 unsigned section_line,
307                 const char *lvalue,
308                 int ltype,
309                 const char *rvalue,
310                 void *data,
311                 void *userdata) {
312         Network *network = userdata;
313         NetDev *netdev;
314         int r;
315
316         assert(filename);
317         assert(lvalue);
318         assert(rvalue);
319         assert(data);
320
321         r = netdev_get(network->manager, rvalue, &netdev);
322         if (r < 0) {
323                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
324                            "VLAN is invalid, ignoring assignment: %s", rvalue);
325                 return 0;
326         }
327
328         if (netdev->kind != NETDEV_KIND_VLAN) {
329                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
330                            "NetDev is not a VLAN, ignoring assignment: %s", rvalue);
331                 return 0;
332         }
333
334         r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
335         if (r < 0) {
336                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
337                            "Can not add VLAN to network: %s", rvalue);
338                 return 0;
339         }
340
341         return 0;
342 }
343
344 int config_parse_macvlan(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                            "MACVLAN is invalid, ignoring assignment: %s", rvalue);
367                 return 0;
368         }
369
370         if (netdev->kind != NETDEV_KIND_MACVLAN) {
371                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
372                            "NetDev is not a MACVLAN, ignoring assignment: %s", rvalue);
373                 return 0;
374         }
375
376         r = hashmap_put(network->macvlans, netdev->name, netdev);
377         if (r < 0) {
378                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
379                            "Can not add MACVLAN to network: %s", rvalue);
380                 return 0;
381         }
382
383         return 0;
384 }