chiark / gitweb /
252c9a0ab0bd0db9d7fbb0bc504a82d82ba154fb
[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_path(filename)) {
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(uint64_hash_func, uint64_compare_func);
70         if (!network->macvlans)
71                 return log_oom();
72
73         network->vxlans = hashmap_new(uint64_hash_func, uint64_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, network);
100         if (r < 0) {
101                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
102                 return r;
103         }
104
105         LIST_PREPEND(networks, manager->networks, network);
106
107         LIST_FOREACH(routes, route, network->static_routes) {
108                 if (!route->family) {
109                         log_warning("Route section without Gateway field configured in %s. "
110                                     "Ignoring", filename);
111                         return 0;
112                 }
113         }
114
115         LIST_FOREACH(addresses, address, network->static_addresses) {
116                 if (!address->family) {
117                         log_warning("Address section without Address field configured in %s. "
118                                     "Ignoring", filename);
119                         return 0;
120                 }
121         }
122
123         network = NULL;
124
125         return 0;
126 }
127
128 int network_load(Manager *manager) {
129         Network *network;
130         _cleanup_strv_free_ char **files = NULL;
131         char **f;
132         int r;
133
134         assert(manager);
135
136         while ((network = manager->networks))
137                 network_free(network);
138
139         r = conf_files_list_strv(&files, ".network", NULL, network_dirs);
140         if (r < 0) {
141                 log_error("Failed to enumerate network files: %s", strerror(-r));
142                 return r;
143         }
144
145         STRV_FOREACH_BACKWARDS(f, files) {
146                 r = network_load_one(manager, *f);
147                 if (r < 0)
148                         return r;
149         }
150
151         return 0;
152 }
153
154 void network_free(Network *network) {
155         NetDev *netdev;
156         Route *route;
157         Address *address;
158         Iterator i;
159
160         if (!network)
161                 return;
162
163         free(network->filename);
164
165         free(network->match_mac);
166         free(network->match_path);
167         free(network->match_driver);
168         free(network->match_type);
169         free(network->match_name);
170
171         free(network->description);
172         free(network->dhcp_vendor_class_identifier);
173
174         while ((address = network->ntp)) {
175                 LIST_REMOVE(addresses, network->ntp, address);
176                 address_free(address);
177         }
178
179         while ((address = network->dns)) {
180                 LIST_REMOVE(addresses, network->dns, address);
181                 address_free(address);
182         }
183
184         netdev_unref(network->bridge);
185
186         netdev_unref(network->bond);
187
188         netdev_unref(network->tunnel);
189
190         HASHMAP_FOREACH(netdev, network->vlans, i)
191                 netdev_unref(netdev);
192         hashmap_free(network->vlans);
193
194         HASHMAP_FOREACH(netdev, network->macvlans, i)
195                 netdev_unref(netdev);
196         hashmap_free(network->macvlans);
197
198         HASHMAP_FOREACH(netdev, network->vxlans, i)
199                 netdev_unref(netdev);
200         hashmap_free(network->vxlans);
201
202         while ((route = network->static_routes))
203                 route_free(route);
204
205         while ((address = network->static_addresses))
206                 address_free(address);
207
208         hashmap_free(network->addresses_by_section);
209         hashmap_free(network->routes_by_section);
210
211         if (network->manager && network->manager->networks)
212                 LIST_REMOVE(networks, network->manager->networks, network);
213
214         condition_free_list(network->match_host);
215         condition_free_list(network->match_virt);
216         condition_free_list(network->match_kernel);
217         condition_free_list(network->match_arch);
218
219         free(network);
220 }
221
222 int network_get(Manager *manager, struct udev_device *device,
223                 const char *ifname, const struct ether_addr *address,
224                 Network **ret) {
225         Network *network;
226
227         assert(manager);
228         assert(ret);
229
230         LIST_FOREACH(networks, network, manager->networks) {
231                 if (net_match_config(network->match_mac, network->match_path,
232                                      network->match_driver, network->match_type,
233                                      network->match_name, network->match_host,
234                                      network->match_virt, network->match_kernel,
235                                      network->match_arch,
236                                      address,
237                                      udev_device_get_property_value(device, "ID_PATH"),
238                                      udev_device_get_driver(udev_device_get_parent(device)),
239                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
240                                      udev_device_get_devtype(device),
241                                      ifname)) {
242                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
243                                   network->filename);
244                         *ret = network;
245                         return 0;
246                 }
247         }
248
249         *ret = NULL;
250
251         return -ENOENT;
252 }
253
254 int network_apply(Manager *manager, Network *network, Link *link) {
255         int r;
256
257         link->network = network;
258
259         if (network->dns || network->ntp) {
260                 r = link_save(link);
261                 if (r < 0)
262                         return r;
263         }
264
265         return 0;
266 }
267
268 int config_parse_netdev(const char *unit,
269                 const char *filename,
270                 unsigned line,
271                 const char *section,
272                 unsigned section_line,
273                 const char *lvalue,
274                 int ltype,
275                 const char *rvalue,
276                 void *data,
277                 void *userdata) {
278         Network *network = userdata;
279         _cleanup_free_ char *kind_string = NULL;
280         char *p;
281         NetDev *netdev;
282         NetDevKind kind;
283         int r;
284
285         assert(filename);
286         assert(lvalue);
287         assert(rvalue);
288         assert(data);
289
290         kind_string = strdup(lvalue);
291         if (!kind_string)
292                 return log_oom();
293
294         /* the keys are CamelCase versions of the kind */
295         for (p = kind_string; *p; p++)
296                 *p = tolower(*p);
297
298         kind = netdev_kind_from_string(kind_string);
299         if (kind == _NETDEV_KIND_INVALID) {
300                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
301                            "Invalid NetDev kind: %s", lvalue);
302                 return 0;
303         }
304
305         r = netdev_get(network->manager, rvalue, &netdev);
306         if (r < 0) {
307                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
308                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
309                 return 0;
310         }
311
312         if (netdev->kind != kind) {
313                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
314                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
315                 return 0;
316         }
317
318         switch (kind) {
319         case NETDEV_KIND_BRIDGE:
320                 network->bridge = netdev;
321
322                 break;
323         case NETDEV_KIND_BOND:
324                 network->bond = netdev;
325
326                 break;
327         case NETDEV_KIND_VLAN:
328                 r = hashmap_put(network->vlans, &netdev->vlanid, netdev);
329                 if (r < 0) {
330                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
331                                    "Can not add VLAN to network: %s", rvalue);
332                         return 0;
333                 }
334
335                 break;
336         case NETDEV_KIND_MACVLAN:
337                 r = hashmap_put(network->macvlans, netdev->ifname, netdev);
338                 if (r < 0) {
339                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
340                                    "Can not add MACVLAN to network: %s", rvalue);
341                         return 0;
342                 }
343
344                 break;
345         case NETDEV_KIND_VXLAN:
346                 r = hashmap_put(network->vxlans, netdev->ifname, netdev);
347                 if (r < 0) {
348                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
349                                    "Can not add VXLAN to network: %s", rvalue);
350                         return 0;
351                 }
352
353                 break;
354         default:
355                 assert_not_reached("Can not parse NetDev");
356         }
357
358         netdev_ref(netdev);
359
360         return 0;
361 }
362
363 int config_parse_tunnel(const char *unit,
364                         const char *filename,
365                         unsigned line,
366                         const char *section,
367                         unsigned section_line,
368                         const char *lvalue,
369                         int ltype,
370                         const char *rvalue,
371                         void *data,
372                         void *userdata) {
373         Network *network = userdata;
374         NetDev *netdev;
375         int r;
376
377         assert(filename);
378         assert(lvalue);
379         assert(rvalue);
380         assert(data);
381
382         r = netdev_get(network->manager, rvalue, &netdev);
383         if (r < 0) {
384                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
385                            "Tunnel is invalid, ignoring assignment: %s", rvalue);
386                 return 0;
387         }
388
389         if (netdev->kind != NETDEV_KIND_IPIP &&
390             netdev->kind != NETDEV_KIND_SIT &&
391             netdev->kind != NETDEV_KIND_GRE &&
392             netdev->kind != NETDEV_KIND_VTI) {
393                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
394                            "NetDev is not a tunnel, ignoring assignment: %s", rvalue);
395                 return 0;
396         }
397
398         network->tunnel = netdev;
399
400         return 0;
401 }