chiark / gitweb /
units: set 'AllowUser=root own' and 'AllowWorld=talk' own for all .busname files
[elogind.git] / src / network / networkd-netdev.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 "list.h"
28
29 #define VLANID_MAX 4094
30
31 static const char* const netdev_kind_table[] = {
32         [NETDEV_KIND_BRIDGE] = "bridge",
33         [NETDEV_KIND_BOND] = "bond",
34         [NETDEV_KIND_VLAN] = "vlan",
35         [NETDEV_KIND_MACVLAN] = "macvlan",
36 };
37
38 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
39 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
40
41 static const char* const macvlan_mode_table[] = {
42         [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
43         [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
44         [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
45         [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
46 };
47
48 DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
49 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
50
51 void netdev_free(NetDev *netdev) {
52         netdev_enslave_callback *callback;
53
54         if (!netdev)
55                 return;
56
57         while ((callback = netdev->callbacks)) {
58                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
59                 free(callback);
60         }
61
62         if (netdev->name)
63                 hashmap_remove(netdev->manager->netdevs, netdev->name);
64
65         free(netdev->filename);
66
67         free(netdev->description);
68         free(netdev->name);
69
70         free(netdev);
71 }
72
73 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
74         NetDev *netdev;
75
76         assert(manager);
77         assert(name);
78         assert(ret);
79
80         netdev = hashmap_get(manager->netdevs, name);
81         if (!netdev) {
82                 *ret = NULL;
83                 return -ENOENT;
84         }
85
86         *ret = netdev;
87
88         return 0;
89 }
90
91 static int netdev_enter_failed(NetDev *netdev) {
92         netdev->state = NETDEV_STATE_FAILED;
93
94         return 0;
95 }
96
97 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
98         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
99         int r;
100
101         assert(netdev);
102         assert(netdev->state == NETDEV_STATE_READY);
103         assert(netdev->manager);
104         assert(netdev->manager->rtnl);
105         assert(link);
106         assert(callback);
107
108         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
109                                      RTM_SETLINK, link->ifindex);
110         if (r < 0) {
111                 log_error_netdev(netdev,
112                                  "Could not allocate RTM_SETLINK message: %s",
113                                  strerror(-r));
114                 return r;
115         }
116
117         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
118         if (r < 0) {
119                 log_error_netdev(netdev,
120                                  "Could not append IFLA_MASTER attribute: %s",
121                                  strerror(-r));
122                 return r;
123         }
124
125         r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
126         if (r < 0) {
127                 log_error_netdev(netdev,
128                                  "Could not send rtnetlink message: %s",
129                                  strerror(-r));
130                 return r;
131         }
132
133         log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
134
135         return 0;
136 }
137
138 static int netdev_enter_ready(NetDev *netdev) {
139         netdev_enslave_callback *callback;
140
141         assert(netdev);
142         assert(netdev->name);
143
144         netdev->state = NETDEV_STATE_READY;
145
146         log_info_netdev(netdev, "netdev ready");
147
148         LIST_FOREACH(callbacks, callback, netdev->callbacks) {
149                 /* enslave the links that were attempted to be enslaved befor the
150                  * link was ready */
151                 netdev_enslave_ready(netdev, callback->link, callback->callback);
152         }
153
154         return 0;
155 }
156
157 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
158         NetDev *netdev = userdata;
159         int r;
160
161         assert(netdev->state != _NETDEV_STATE_INVALID);
162
163         r = sd_rtnl_message_get_errno(m);
164         if (r < 0) {
165                 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
166                 netdev_enter_failed(netdev);
167
168                 return 1;
169         }
170
171         return 1;
172 }
173
174 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
175         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
176         const char *kind;
177         int r;
178
179         assert(netdev);
180         assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
181                (link && callback));
182         assert(netdev->name);
183         assert(netdev->manager);
184         assert(netdev->manager->rtnl);
185
186         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
187         if (r < 0) {
188                 log_error_netdev(netdev,
189                                  "Could not allocate RTM_NEWLINK message: %s",
190                                  strerror(-r));
191                 return r;
192         }
193
194         if (link) {
195                 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
196                 if (r < 0) {
197                         log_error_netdev(netdev,
198                                          "Could not append IFLA_LINK attribute: %s",
199                                          strerror(-r));
200                         return r;
201                 }
202         }
203
204         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
205         if (r < 0) {
206                 log_error_netdev(netdev,
207                                  "Could not append IFLA_IFNAME attribute: %s",
208                                  strerror(-r));
209                 return r;
210         }
211
212         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
213         if (r < 0) {
214                 log_error_netdev(netdev,
215                                  "Could not open IFLA_LINKINFO container: %s",
216                                  strerror(-r));
217                 return r;
218         }
219
220         kind = netdev_kind_to_string(netdev->kind);
221         if (!kind) {
222                 log_error_netdev(netdev, "Invalid kind");
223                 return -EINVAL;
224         }
225
226         r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
227         if (r < 0) {
228                 log_error_netdev(netdev,
229                                  "Could not append IFLA_INFO_KIND attribute: %s",
230                                  strerror(-r));
231                 return r;
232         }
233
234         if (netdev->vlanid <= VLANID_MAX || netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
235                 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
236                 if (r < 0) {
237                         log_error_netdev(netdev,
238                                          "Could not open IFLA_INFO_DATA container: %s",
239                                          strerror(-r));
240                         return r;
241                 }
242
243                 if (netdev->vlanid <= VLANID_MAX) {
244                         r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
245                         if (r < 0) {
246                                 log_error_netdev(netdev,
247                                                  "Could not append IFLA_VLAN_ID attribute: %s",
248                                                  strerror(-r));
249                                 return r;
250                         }
251                 }
252
253                 if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
254                         r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
255                         if (r < 0) {
256                                 log_error_netdev(netdev,
257                                                  "Could not append IFLA_MACVLAN_MODE attribute: %s",
258                                                  strerror(-r));
259                                 return r;
260                         }
261                 }
262
263                 r = sd_rtnl_message_close_container(req);
264                 if (r < 0) {
265                         log_error_netdev(netdev,
266                                          "Could not close IFLA_INFO_DATA container %s",
267                                          strerror(-r));
268                         return r;
269                 }
270         }
271
272         r = sd_rtnl_message_close_container(req);
273         if (r < 0) {
274                 log_error_netdev(netdev,
275                                  "Could not close IFLA_LINKINFO container %s",
276                                  strerror(-r));
277                 return r;
278         }
279
280         if (link)
281                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
282         else
283                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
284         if (r < 0) {
285                 log_error_netdev(netdev,
286                                  "Could not send rtnetlink message: %s", strerror(-r));
287                 return r;
288         }
289
290         log_debug_netdev(netdev, "creating netdev");
291
292         netdev->state = NETDEV_STATE_CREATING;
293
294         return 0;
295 }
296
297 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
298         if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
299                 return netdev_create(netdev, link, callback);
300
301         if (netdev->state == NETDEV_STATE_READY) {
302                 netdev_enslave_ready(netdev, link, callback);
303         } else {
304                 /* the netdev is not yet read, save this request for when it is*/
305                 netdev_enslave_callback *cb;
306
307                 cb = new0(netdev_enslave_callback, 1);
308                 if (!cb)
309                         return log_oom();
310
311                 cb->callback = callback;
312                 cb->link = link;
313
314                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
315         }
316
317         return 0;
318 }
319
320 int netdev_set_ifindex(NetDev *netdev, int ifindex) {
321         assert(netdev);
322         assert(ifindex > 0);
323
324         if (netdev->ifindex > 0) {
325                 if (netdev->ifindex == ifindex)
326                         return 0;
327                 else
328                         return -EEXIST;
329         }
330
331         netdev->ifindex = ifindex;
332
333         netdev_enter_ready(netdev);
334
335         return 0;
336 }
337
338 static int netdev_load_one(Manager *manager, const char *filename) {
339         _cleanup_netdev_free_ NetDev *netdev = NULL;
340         _cleanup_fclose_ FILE *file = NULL;
341         int r;
342
343         assert(manager);
344         assert(filename);
345
346         file = fopen(filename, "re");
347         if (!file) {
348                 if (errno == ENOENT)
349                         return 0;
350                 else
351                         return errno;
352         }
353
354         netdev = new0(NetDev, 1);
355         if (!netdev)
356                 return log_oom();
357
358         netdev->manager = manager;
359         netdev->state = _NETDEV_STATE_INVALID;
360         netdev->kind = _NETDEV_KIND_INVALID;
361         netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
362         netdev->vlanid = VLANID_MAX + 1;
363
364         r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
365                          config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
366                          false, false, netdev);
367         if (r < 0) {
368                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
369                 return r;
370         }
371
372         if (netdev->kind == _NETDEV_KIND_INVALID) {
373                 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
374                 return 0;
375         }
376
377         if (!netdev->name) {
378                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
379                 return 0;
380         }
381
382         if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
383                 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
384                 return 0;
385         }
386
387         if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
388                 log_warning("VLAN Id configured for a %s in %s. Ignoring",
389                             netdev_kind_to_string(netdev->kind), filename);
390                 return 0;
391         }
392
393         if (netdev->kind != NETDEV_KIND_MACVLAN &&
394             netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
395                 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
396                             netdev_kind_to_string(netdev->kind), filename);
397                 return 0;
398         }
399
400         netdev->filename = strdup(filename);
401         if (!netdev->filename)
402                 return log_oom();
403
404         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
405                              netdev->match_host, netdev->match_virt,
406                              netdev->match_kernel, netdev->match_arch,
407                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
408                 return 0;
409
410         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
411         if (r < 0)
412                 return r;
413
414         LIST_HEAD_INIT(netdev->callbacks);
415
416         if (netdev->kind != NETDEV_KIND_VLAN &&
417             netdev->kind != NETDEV_KIND_MACVLAN) {
418                 r = netdev_create(netdev, NULL, NULL);
419                 if (r < 0)
420                         return r;
421         }
422
423         netdev = NULL;
424
425         return 0;
426 }
427
428 int netdev_load(Manager *manager) {
429         NetDev *netdev;
430         char **files, **f;
431         int r;
432
433         assert(manager);
434
435         while ((netdev = hashmap_first(manager->netdevs)))
436                 netdev_free(netdev);
437
438         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
439         if (r < 0) {
440                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
441                 return r;
442         }
443
444         STRV_FOREACH_BACKWARDS(f, files) {
445                 r = netdev_load_one(manager, *f);
446                 if (r < 0)
447                         return r;
448         }
449
450         strv_free(files);
451
452         return 0;
453 }