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