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