chiark / gitweb /
networkd: store ifindex as int
[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->ipv4ll_route = true;
90
91         network->dhcp_ntp = true;
92         network->dhcp_dns = true;
93         network->dhcp_hostname = true;
94         network->dhcp_domainname = true;
95         network->dhcp_routes = true;
96         network->dhcp_sendhost = true;
97
98         r = config_parse(NULL, filename, file,
99                          "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0",
100                          config_item_perf_lookup, network_network_gperf_lookup,
101                          false, false, true, network);
102         if (r < 0)
103                 return r;
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         strv_free(network->ntp);
175         strv_free(network->dns);
176
177         netdev_unref(network->bridge);
178
179         netdev_unref(network->bond);
180
181         netdev_unref(network->tunnel);
182
183         HASHMAP_FOREACH(netdev, network->vlans, i)
184                 netdev_unref(netdev);
185         hashmap_free(network->vlans);
186
187         HASHMAP_FOREACH(netdev, network->macvlans, i)
188                 netdev_unref(netdev);
189         hashmap_free(network->macvlans);
190
191         HASHMAP_FOREACH(netdev, network->vxlans, i)
192                 netdev_unref(netdev);
193         hashmap_free(network->vxlans);
194
195         while ((route = network->static_routes))
196                 route_free(route);
197
198         while ((address = network->static_addresses))
199                 address_free(address);
200
201         hashmap_free(network->addresses_by_section);
202         hashmap_free(network->routes_by_section);
203
204         if (network->manager && network->manager->networks)
205                 LIST_REMOVE(networks, network->manager->networks, network);
206
207         condition_free_list(network->match_host);
208         condition_free_list(network->match_virt);
209         condition_free_list(network->match_kernel);
210         condition_free_list(network->match_arch);
211
212         free(network);
213 }
214
215 int network_get(Manager *manager, struct udev_device *device,
216                 const char *ifname, const struct ether_addr *address,
217                 Network **ret) {
218         Network *network;
219
220         assert(manager);
221         assert(ret);
222
223         LIST_FOREACH(networks, network, manager->networks) {
224                 if (net_match_config(network->match_mac, network->match_path,
225                                      network->match_driver, network->match_type,
226                                      network->match_name, network->match_host,
227                                      network->match_virt, network->match_kernel,
228                                      network->match_arch,
229                                      address,
230                                      udev_device_get_property_value(device, "ID_PATH"),
231                                      udev_device_get_driver(udev_device_get_parent(device)),
232                                      udev_device_get_property_value(device, "ID_NET_DRIVER"),
233                                      udev_device_get_devtype(device),
234                                      ifname)) {
235                         log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname,
236                                   network->filename);
237                         *ret = network;
238                         return 0;
239                 }
240         }
241
242         *ret = NULL;
243
244         return -ENOENT;
245 }
246
247 int network_apply(Manager *manager, Network *network, Link *link) {
248         int r;
249
250         link->network = network;
251
252         if (network->ipv4ll_route) {
253                 Route *route;
254
255                 r = route_new_static(network, 0, &route);
256                 if (r < 0)
257                         return r;
258
259                 r = inet_pton(AF_INET, "169.254.0.0", &route->dst_addr.in);
260                 if (r == 0)
261                         return -EINVAL;
262                 if (r < 0)
263                         return -errno;
264
265                 route->family = AF_INET;
266                 route->dst_prefixlen = 16;
267                 route->scope = RT_SCOPE_LINK;
268                 route->metrics = IPV4LL_ROUTE_METRIC;
269                 route->protocol = RTPROT_STATIC;
270         }
271
272         if (network->dns || network->ntp) {
273                 r = link_save(link);
274                 if (r < 0)
275                         return r;
276         }
277
278         return 0;
279 }
280
281 int config_parse_netdev(const char *unit,
282                 const char *filename,
283                 unsigned line,
284                 const char *section,
285                 unsigned section_line,
286                 const char *lvalue,
287                 int ltype,
288                 const char *rvalue,
289                 void *data,
290                 void *userdata) {
291         Network *network = userdata;
292         _cleanup_free_ char *kind_string = NULL;
293         char *p;
294         NetDev *netdev;
295         NetDevKind kind;
296         int r;
297
298         assert(filename);
299         assert(lvalue);
300         assert(rvalue);
301         assert(data);
302
303         kind_string = strdup(lvalue);
304         if (!kind_string)
305                 return log_oom();
306
307         /* the keys are CamelCase versions of the kind */
308         for (p = kind_string; *p; p++)
309                 *p = tolower(*p);
310
311         kind = netdev_kind_from_string(kind_string);
312         if (kind == _NETDEV_KIND_INVALID) {
313                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
314                            "Invalid NetDev kind: %s", lvalue);
315                 return 0;
316         }
317
318         r = netdev_get(network->manager, rvalue, &netdev);
319         if (r < 0) {
320                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
321                            "%s could not be found, ignoring assignment: %s", lvalue, rvalue);
322                 return 0;
323         }
324
325         if (netdev->kind != kind) {
326                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
327                            "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue);
328                 return 0;
329         }
330
331         switch (kind) {
332         case NETDEV_KIND_BRIDGE:
333                 network->bridge = netdev;
334
335                 break;
336         case NETDEV_KIND_BOND:
337                 network->bond = netdev;
338
339                 break;
340         case NETDEV_KIND_VLAN:
341                 r = hashmap_put(network->vlans, netdev->ifname, netdev);
342                 if (r < 0) {
343                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
344                                    "Can not add VLAN '%s' to network: %s",
345                                    rvalue, strerror(-r));
346                         return 0;
347                 }
348
349                 break;
350         case NETDEV_KIND_MACVLAN:
351                 r = hashmap_put(network->macvlans, netdev->ifname, netdev);
352                 if (r < 0) {
353                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
354                                    "Can not add MACVLAN '%s' to network: %s",
355                                    rvalue, strerror(-r));
356                         return 0;
357                 }
358
359                 break;
360         case NETDEV_KIND_VXLAN:
361                 r = hashmap_put(network->vxlans, netdev->ifname, netdev);
362                 if (r < 0) {
363                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
364                                    "Can not add VXLAN '%s' to network: %s",
365                                    rvalue, strerror(-r));
366                         return 0;
367                 }
368
369                 break;
370         default:
371                 assert_not_reached("Can not parse NetDev");
372         }
373
374         netdev_ref(netdev);
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         network->tunnel = netdev;
415
416         return 0;
417 }