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