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