chiark / gitweb /
networkd: introduce refcounting for Links and NetDevs
[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 "network-internal.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[_NETDEV_KIND_MAX] = {
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[_NETDEV_MACVLAN_MODE_MAX] = {
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 static 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         condition_free_list(netdev->match_host);
71         condition_free_list(netdev->match_virt);
72         condition_free_list(netdev->match_kernel);
73         condition_free_list(netdev->match_arch);
74
75         free(netdev);
76 }
77
78 NetDev *netdev_unref(NetDev *netdev) {
79         if (netdev && (-- netdev->n_ref <= 0))
80                 netdev_free(netdev);
81
82         return NULL;
83 }
84
85 NetDev *netdev_ref(NetDev *netdev) {
86         if (netdev)
87                 assert_se(++ netdev->n_ref >= 2);
88
89         return netdev;
90 }
91
92 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
93         NetDev *netdev;
94
95         assert(manager);
96         assert(name);
97         assert(ret);
98
99         netdev = hashmap_get(manager->netdevs, name);
100         if (!netdev) {
101                 *ret = NULL;
102                 return -ENOENT;
103         }
104
105         *ret = netdev;
106
107         return 0;
108 }
109
110 static int netdev_enter_failed(NetDev *netdev) {
111         netdev->state = NETDEV_STATE_FAILED;
112
113         return 0;
114 }
115
116 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
117         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
118         int r;
119
120         assert(netdev);
121         assert(netdev->state == NETDEV_STATE_READY);
122         assert(netdev->manager);
123         assert(netdev->manager->rtnl);
124         assert(link);
125         assert(callback);
126
127         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
128                                      RTM_SETLINK, link->ifindex);
129         if (r < 0) {
130                 log_error_netdev(netdev,
131                                  "Could not allocate RTM_SETLINK message: %s",
132                                  strerror(-r));
133                 return r;
134         }
135
136         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
137         if (r < 0) {
138                 log_error_netdev(netdev,
139                                  "Could not append IFLA_MASTER attribute: %s",
140                                  strerror(-r));
141                 return r;
142         }
143
144         r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
145         if (r < 0) {
146                 log_error_netdev(netdev,
147                                  "Could not send rtnetlink message: %s",
148                                  strerror(-r));
149                 return r;
150         }
151
152         log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
153
154         return 0;
155 }
156
157 static int netdev_enter_ready(NetDev *netdev) {
158         netdev_enslave_callback *callback;
159
160         assert(netdev);
161         assert(netdev->name);
162
163         if (netdev->state != NETDEV_STATE_CREATING)
164                 return 0;
165
166         netdev->state = NETDEV_STATE_READY;
167
168         log_info_netdev(netdev, "netdev ready");
169
170         LIST_FOREACH(callbacks, callback, netdev->callbacks) {
171                 /* enslave the links that were attempted to be enslaved befor the
172                  * link was ready */
173                 netdev_enslave_ready(netdev, callback->link, callback->callback);
174         }
175
176         return 0;
177 }
178 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
179         NetDev *netdev = userdata;
180         int r;
181
182         assert(netdev->state != _NETDEV_STATE_INVALID);
183
184         r = sd_rtnl_message_get_errno(m);
185         if (r == -EEXIST)
186                 log_debug_netdev(netdev, "netdev exists, using existing");
187         else if (r < 0) {
188                 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
189                 netdev_enter_failed(netdev);
190
191                 return 1;
192         }
193
194         return 1;
195 }
196
197 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
198         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
199         const char *kind;
200         int r;
201
202         assert(netdev);
203         assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
204                (link && callback));
205         assert(netdev->name);
206         assert(netdev->manager);
207         assert(netdev->manager->rtnl);
208
209         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
210         if (r < 0) {
211                 log_error_netdev(netdev,
212                                  "Could not allocate RTM_NEWLINK message: %s",
213                                  strerror(-r));
214                 return r;
215         }
216
217         if (link) {
218                 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
219                 if (r < 0) {
220                         log_error_netdev(netdev,
221                                          "Could not append IFLA_LINK attribute: %s",
222                                          strerror(-r));
223                         return r;
224                 }
225         }
226
227         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
228         if (r < 0) {
229                 log_error_netdev(netdev,
230                                  "Could not append IFLA_IFNAME attribute: %s",
231                                  strerror(-r));
232                 return r;
233         }
234
235         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
236         if (r < 0) {
237                 log_error_netdev(netdev,
238                                  "Could not open IFLA_LINKINFO container: %s",
239                                  strerror(-r));
240                 return r;
241         }
242
243         kind = netdev_kind_to_string(netdev->kind);
244         if (!kind) {
245                 log_error_netdev(netdev, "Invalid kind");
246                 return -EINVAL;
247         }
248
249         r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
250         if (r < 0) {
251                 log_error_netdev(netdev,
252                                  "Could not open IFLA_INFO_DATA container: %s",
253                                   strerror(-r));
254                 return r;
255         }
256
257         if (netdev->vlanid <= VLANID_MAX) {
258                 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
259                 if (r < 0) {
260                         log_error_netdev(netdev,
261                                          "Could not append IFLA_VLAN_ID attribute: %s",
262                                          strerror(-r));
263                         return r;
264                 }
265         }
266
267         if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
268         r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
269         if (r < 0) {
270                 log_error_netdev(netdev,
271                                  "Could not append IFLA_MACVLAN_MODE attribute: %s",
272                                  strerror(-r));
273                         return r;
274                 }
275         }
276
277         r = sd_rtnl_message_close_container(req);
278         if (r < 0) {
279                 log_error_netdev(netdev,
280                                  "Could not close IFLA_INFO_DATA container %s",
281                                  strerror(-r));
282                 return r;
283         }
284
285         r = sd_rtnl_message_close_container(req);
286         if (r < 0) {
287                 log_error_netdev(netdev,
288                                  "Could not close IFLA_LINKINFO container %s",
289                                  strerror(-r));
290                 return r;
291         }
292
293         if (link)
294                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
295         else
296                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
297         if (r < 0) {
298                 log_error_netdev(netdev,
299                                  "Could not send rtnetlink message: %s", strerror(-r));
300                 return r;
301         }
302
303         log_debug_netdev(netdev, "creating netdev");
304
305         netdev->state = NETDEV_STATE_CREATING;
306
307         return 0;
308 }
309
310 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
311         if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
312                 return netdev_create(netdev, link, callback);
313
314         if (netdev->state == NETDEV_STATE_READY) {
315                 netdev_enslave_ready(netdev, link, callback);
316         } else {
317                 /* the netdev is not yet read, save this request for when it is*/
318                 netdev_enslave_callback *cb;
319
320                 cb = new0(netdev_enslave_callback, 1);
321                 if (!cb)
322                         return log_oom();
323
324                 cb->callback = callback;
325                 cb->link = link;
326
327                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
328         }
329
330         return 0;
331 }
332
333 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
334         uint16_t type;
335         const char *kind;
336         char *received_kind;
337         char *received_name;
338         int r, ifindex;
339
340         assert(netdev);
341         assert(message);
342
343         r = sd_rtnl_message_get_type(message, &type);
344         if (r < 0) {
345                 log_error_netdev(netdev, "Could not get rtnl message type");
346                 return r;
347         }
348
349         if (type != RTM_NEWLINK) {
350                 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
351                 return -EINVAL;
352         }
353
354         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
355         if (r < 0) {
356                 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
357                 netdev_enter_failed(netdev);
358                 return r;
359         } else if (ifindex <= 0) {
360                 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
361                 netdev_enter_failed(netdev);
362                 return r;
363         }
364
365
366         if (netdev->ifindex > 0) {
367                 if (netdev->ifindex != ifindex) {
368                         log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
369                                          ifindex, netdev->ifindex);
370                         netdev_enter_failed(netdev);
371                         return -EEXIST;
372                 } else
373                         /* ifindex already set to the same for this netdev */
374                         return 0;
375         }
376
377         r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
378         if (r < 0) {
379                 log_error_netdev(netdev, "Could not get IFNAME");
380                 return r;
381         }
382
383         if (!streq(netdev->name, received_name)) {
384                 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
385                                  received_name);
386                 netdev_enter_failed(netdev);
387                 return r;
388         }
389
390         r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
391         if (r < 0) {
392                 log_error_netdev(netdev, "Could not get LINKINFO");
393                 return r;
394         }
395
396         r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
397         if (r < 0) {
398                 log_error_netdev(netdev, "Could not get KIND");
399                 return r;
400         }
401
402         r = sd_rtnl_message_exit_container(message);
403         if (r < 0) {
404                 log_error_netdev(netdev, "Could not exit container");
405                 return r;
406         }
407
408         kind = netdev_kind_to_string(netdev->kind);
409         if (!kind) {
410                 log_error_netdev(netdev, "Could not get kind");
411                 netdev_enter_failed(netdev);
412                 return -EINVAL;
413         }
414
415         if (!streq(kind, received_kind)) {
416                 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
417                                  "expected %s", received_kind, kind);
418                 netdev_enter_failed(netdev);
419                 return r;
420         }
421
422         netdev->ifindex = ifindex;
423
424         netdev_enter_ready(netdev);
425
426         return 0;
427 }
428
429 static int netdev_load_one(Manager *manager, const char *filename) {
430         _cleanup_netdev_unref_ NetDev *netdev = NULL;
431         _cleanup_fclose_ FILE *file = NULL;
432         int r;
433
434         assert(manager);
435         assert(filename);
436
437         if (null_or_empty_path(filename)) {
438                 log_debug("skipping empty file: %s", filename);
439                 return 0;
440         }
441
442         file = fopen(filename, "re");
443         if (!file) {
444                 if (errno == ENOENT)
445                         return 0;
446                 else
447                         return -errno;
448         }
449
450         netdev = new0(NetDev, 1);
451         if (!netdev)
452                 return log_oom();
453
454         netdev->n_ref = 1;
455         netdev->manager = manager;
456         netdev->state = _NETDEV_STATE_INVALID;
457         netdev->kind = _NETDEV_KIND_INVALID;
458         netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
459         netdev->vlanid = VLANID_MAX + 1;
460
461         r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
462                          config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
463                          false, false, netdev);
464         if (r < 0) {
465                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
466                 return r;
467         }
468
469         if (netdev->kind == _NETDEV_KIND_INVALID) {
470                 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
471                 return 0;
472         }
473
474         if (!netdev->name) {
475                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
476                 return 0;
477         }
478
479         if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
480                 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
481                 return 0;
482         }
483
484         if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
485                 log_warning("VLAN Id configured for a %s in %s. Ignoring",
486                             netdev_kind_to_string(netdev->kind), filename);
487                 return 0;
488         }
489
490         if (netdev->kind != NETDEV_KIND_MACVLAN &&
491             netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
492                 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
493                             netdev_kind_to_string(netdev->kind), filename);
494                 return 0;
495         }
496
497         netdev->filename = strdup(filename);
498         if (!netdev->filename)
499                 return log_oom();
500
501         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
502                              netdev->match_host, netdev->match_virt,
503                              netdev->match_kernel, netdev->match_arch,
504                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
505                 return 0;
506
507         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
508         if (r < 0)
509                 return r;
510
511         LIST_HEAD_INIT(netdev->callbacks);
512
513         if (netdev->kind != NETDEV_KIND_VLAN &&
514             netdev->kind != NETDEV_KIND_MACVLAN) {
515                 r = netdev_create(netdev, NULL, NULL);
516                 if (r < 0)
517                         return r;
518         }
519
520         log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
521
522         netdev = NULL;
523
524         return 0;
525 }
526
527 int netdev_load(Manager *manager) {
528         NetDev *netdev;
529         char **files, **f;
530         int r;
531
532         assert(manager);
533
534         while ((netdev = hashmap_first(manager->netdevs)))
535                 netdev_unref(netdev);
536
537         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
538         if (r < 0) {
539                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
540                 return r;
541         }
542
543         STRV_FOREACH_BACKWARDS(f, files) {
544                 r = netdev_load_one(manager, *f);
545                 if (r < 0)
546                         return r;
547         }
548
549         strv_free(files);
550
551         return 0;
552 }