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