chiark / gitweb /
hwdb: add pnpid for the T450s touchpad
[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 <net/if.h>
23
24 #include "networkd-netdev.h"
25 #include "networkd-link.h"
26 #include "network-internal.h"
27 #include "conf-files.h"
28 #include "conf-parser.h"
29 #include "list.h"
30 #include "siphash24.h"
31
32 const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
33         [NETDEV_KIND_BRIDGE] = &bridge_vtable,
34         [NETDEV_KIND_BOND] = &bond_vtable,
35         [NETDEV_KIND_VLAN] = &vlan_vtable,
36         [NETDEV_KIND_MACVLAN] = &macvlan_vtable,
37         [NETDEV_KIND_IPVLAN] = &ipvlan_vtable,
38         [NETDEV_KIND_VXLAN] = &vxlan_vtable,
39         [NETDEV_KIND_IPIP] = &ipip_vtable,
40         [NETDEV_KIND_GRE] = &gre_vtable,
41         [NETDEV_KIND_GRETAP] = &gretap_vtable,
42         [NETDEV_KIND_IP6GRE] = &ip6gre_vtable,
43         [NETDEV_KIND_IP6GRETAP] = &ip6gretap_vtable,
44         [NETDEV_KIND_SIT] = &sit_vtable,
45         [NETDEV_KIND_VTI] = &vti_vtable,
46         [NETDEV_KIND_VETH] = &veth_vtable,
47         [NETDEV_KIND_DUMMY] = &dummy_vtable,
48         [NETDEV_KIND_TUN] = &tun_vtable,
49         [NETDEV_KIND_TAP] = &tap_vtable,
50         [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable,
51 };
52
53 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
54         [NETDEV_KIND_BRIDGE] = "bridge",
55         [NETDEV_KIND_BOND] = "bond",
56         [NETDEV_KIND_VLAN] = "vlan",
57         [NETDEV_KIND_MACVLAN] = "macvlan",
58         [NETDEV_KIND_IPVLAN] = "ipvlan",
59         [NETDEV_KIND_VXLAN] = "vxlan",
60         [NETDEV_KIND_IPIP] = "ipip",
61         [NETDEV_KIND_GRE] = "gre",
62         [NETDEV_KIND_GRETAP] = "gretap",
63         [NETDEV_KIND_IP6GRE] = "ip6gre",
64         [NETDEV_KIND_IP6GRETAP] = "ip6gretap",
65         [NETDEV_KIND_SIT] = "sit",
66         [NETDEV_KIND_VETH] = "veth",
67         [NETDEV_KIND_VTI] = "vti",
68         [NETDEV_KIND_DUMMY] = "dummy",
69         [NETDEV_KIND_TUN] = "tun",
70         [NETDEV_KIND_TAP] = "tap",
71         [NETDEV_KIND_IP6TNL] = "ip6tnl",
72 };
73
74 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
75 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
76
77 static void netdev_cancel_callbacks(NetDev *netdev) {
78         _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
79         netdev_join_callback *callback;
80
81         if (!netdev)
82                 return;
83
84         rtnl_message_new_synthetic_error(-ENODEV, 0, &m);
85
86         while ((callback = netdev->callbacks)) {
87                 if (m) {
88                         assert(callback->link);
89                         assert(callback->callback);
90                         assert(netdev->manager);
91                         assert(netdev->manager->rtnl);
92
93                         callback->callback(netdev->manager->rtnl, m, link);
94                 }
95
96                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
97                 free(callback);
98         }
99 }
100
101 static void netdev_free(NetDev *netdev) {
102         if (!netdev)
103                 return;
104
105         netdev_cancel_callbacks(netdev);
106
107         if (netdev->ifname)
108                 hashmap_remove(netdev->manager->netdevs, netdev->ifname);
109
110         free(netdev->filename);
111
112         free(netdev->description);
113         free(netdev->ifname);
114         free(netdev->mac);
115
116         condition_free_list(netdev->match_host);
117         condition_free_list(netdev->match_virt);
118         condition_free_list(netdev->match_kernel);
119         condition_free_list(netdev->match_arch);
120
121         if (NETDEV_VTABLE(netdev) &&
122             NETDEV_VTABLE(netdev)->done)
123                 NETDEV_VTABLE(netdev)->done(netdev);
124
125         free(netdev);
126 }
127
128 NetDev *netdev_unref(NetDev *netdev) {
129         if (netdev && (-- netdev->n_ref <= 0))
130                 netdev_free(netdev);
131
132         return NULL;
133 }
134
135 NetDev *netdev_ref(NetDev *netdev) {
136         if (netdev)
137                 assert_se(++ netdev->n_ref >= 2);
138
139         return netdev;
140 }
141
142 void netdev_drop(NetDev *netdev) {
143         if (!netdev || netdev->state == NETDEV_STATE_LINGER)
144                 return;
145
146         netdev->state = NETDEV_STATE_LINGER;
147
148         log_netdev_debug(netdev, "netdev removed");
149
150         netdev_cancel_callbacks(netdev);
151
152         netdev_unref(netdev);
153
154         return;
155 }
156
157 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
158         NetDev *netdev;
159
160         assert(manager);
161         assert(name);
162         assert(ret);
163
164         netdev = hashmap_get(manager->netdevs, name);
165         if (!netdev) {
166                 *ret = NULL;
167                 return -ENOENT;
168         }
169
170         *ret = netdev;
171
172         return 0;
173 }
174
175 static int netdev_enter_failed(NetDev *netdev) {
176         netdev->state = NETDEV_STATE_FAILED;
177
178         return 0;
179 }
180
181 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
182         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
183         int r;
184
185         assert(netdev);
186         assert(netdev->state == NETDEV_STATE_READY);
187         assert(netdev->manager);
188         assert(netdev->manager->rtnl);
189         assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
190         assert(link);
191         assert(callback);
192
193         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
194                                      RTM_SETLINK, link->ifindex);
195         if (r < 0) {
196                 log_netdev_error(netdev,
197                                  "Could not allocate RTM_SETLINK message: %s",
198                                  strerror(-r));
199                 return r;
200         }
201
202         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
203         if (r < 0) {
204                 log_netdev_error(netdev,
205                                  "Could not append IFLA_MASTER attribute: %s",
206                                  strerror(-r));
207                 return r;
208         }
209
210         r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
211         if (r < 0) {
212                 log_netdev_error(netdev,
213                                  "Could not send rtnetlink message: %s",
214                                  strerror(-r));
215                 return r;
216         }
217
218         link_ref(link);
219
220         log_netdev_debug(netdev, "enslaving link '%s'", link->ifname);
221
222         return 0;
223 }
224
225 static int netdev_enter_ready(NetDev *netdev) {
226         netdev_join_callback *callback, *callback_next;
227         int r;
228
229         assert(netdev);
230         assert(netdev->ifname);
231
232         if (netdev->state != NETDEV_STATE_CREATING)
233                 return 0;
234
235         netdev->state = NETDEV_STATE_READY;
236
237         log_info_netdev(netdev, "netdev ready");
238
239         LIST_FOREACH_SAFE(callbacks, callback, callback_next, netdev->callbacks) {
240                 /* enslave the links that were attempted to be enslaved before the
241                  * link was ready */
242                 r = netdev_enslave_ready(netdev, callback->link, callback->callback);
243                 if (r < 0)
244                         return r;
245
246                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
247                 link_unref(callback->link);
248                 free(callback);
249         }
250
251         return 0;
252 }
253
254 /* callback for netdev's created without a backing Link */
255 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
256         _cleanup_netdev_unref_ NetDev *netdev = userdata;
257         int r;
258
259         assert(netdev->state != _NETDEV_STATE_INVALID);
260
261         r = sd_rtnl_message_get_errno(m);
262         if (r == -EEXIST)
263                 log_netdev_debug(netdev, "netdev exists, using existing");
264         else if (r < 0) {
265                 log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r));
266                 netdev_drop(netdev);
267
268                 return 1;
269         }
270
271         log_netdev_debug(netdev, "created");
272
273         return 1;
274 }
275
276 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
277         int r;
278
279         assert(netdev);
280         assert(IN_SET(netdev->kind, NETDEV_KIND_BRIDGE, NETDEV_KIND_BOND));
281
282         if (netdev->state == NETDEV_STATE_READY) {
283                 r = netdev_enslave_ready(netdev, link, callback);
284                 if (r < 0)
285                         return r;
286         } else {
287                 /* the netdev is not yet read, save this request for when it is */
288                 netdev_join_callback *cb;
289
290                 cb = new0(netdev_join_callback, 1);
291                 if (!cb)
292                         return log_oom();
293
294                 cb->callback = callback;
295                 cb->link = link;
296                 link_ref(link);
297
298                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
299
300                 log_netdev_debug(netdev, "will enslave '%s', when reday",
301                                  link->ifname);
302         }
303
304         return 0;
305 }
306
307 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
308         uint16_t type;
309         const char *kind;
310         const char *received_kind;
311         const char *received_name;
312         int r, ifindex;
313
314         assert(netdev);
315         assert(message);
316
317         r = sd_rtnl_message_get_type(message, &type);
318         if (r < 0) {
319                 log_netdev_error(netdev, "Could not get rtnl message type");
320                 return r;
321         }
322
323         if (type != RTM_NEWLINK) {
324                 log_netdev_error(netdev, "Can not set ifindex from unexpected rtnl message type");
325                 return -EINVAL;
326         }
327
328         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
329         if (r < 0) {
330                 log_netdev_error(netdev, "Could not get ifindex: %s", strerror(-r));
331                 netdev_enter_failed(netdev);
332                 return r;
333         } else if (ifindex <= 0) {
334                 log_netdev_error(netdev, "Got invalid ifindex: %d", ifindex);
335                 netdev_enter_failed(netdev);
336                 return r;
337         }
338
339         if (netdev->ifindex > 0) {
340                 if (netdev->ifindex != ifindex) {
341                         log_netdev_error(netdev, "Could not set ifindex to %d, already set to %d",
342                                          ifindex, netdev->ifindex);
343                         netdev_enter_failed(netdev);
344                         return -EEXIST;
345                 } else
346                         /* ifindex already set to the same for this netdev */
347                         return 0;
348         }
349
350         r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
351         if (r < 0) {
352                 log_netdev_error(netdev, "Could not get IFNAME");
353                 return r;
354         }
355
356         if (!streq(netdev->ifname, received_name)) {
357                 log_netdev_error(netdev, "Received newlink with wrong IFNAME %s",
358                                  received_name);
359                 netdev_enter_failed(netdev);
360                 return r;
361         }
362
363         r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
364         if (r < 0) {
365                 log_netdev_error(netdev, "Could not get LINKINFO");
366                 return r;
367         }
368
369         r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
370         if (r < 0) {
371                 log_netdev_error(netdev, "Could not get KIND");
372                 return r;
373         }
374
375         r = sd_rtnl_message_exit_container(message);
376         if (r < 0) {
377                 log_netdev_error(netdev, "Could not exit container");
378                 return r;
379         }
380
381         if (netdev->kind == NETDEV_KIND_TAP)
382                 /* the kernel does not distinguish between tun and tap */
383                 kind = "tun";
384         else {
385                 kind = netdev_kind_to_string(netdev->kind);
386                 if (!kind) {
387                         log_netdev_error(netdev, "Could not get kind");
388                         netdev_enter_failed(netdev);
389                         return -EINVAL;
390                 }
391         }
392
393         if (!streq(kind, received_kind)) {
394                 log_netdev_error(netdev,
395                                  "Received newlink with wrong KIND %s, "
396                                  "expected %s", received_kind, kind);
397                 netdev_enter_failed(netdev);
398                 return r;
399         }
400
401         netdev->ifindex = ifindex;
402
403         log_netdev_debug(netdev, "netdev has index %d", netdev->ifindex);
404
405         netdev_enter_ready(netdev);
406
407         return 0;
408 }
409
410 #define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
411
412 int netdev_get_mac(const char *ifname, struct ether_addr **ret) {
413         _cleanup_free_ struct ether_addr *mac = NULL;
414         uint8_t result[8];
415         size_t l, sz;
416         uint8_t *v;
417         int r;
418
419         assert(ifname);
420         assert(ret);
421
422         mac = new0(struct ether_addr, 1);
423         if (!mac)
424                 return -ENOMEM;
425
426         l = strlen(ifname);
427         sz = sizeof(sd_id128_t) + l;
428         v = alloca(sz);
429
430         /* fetch some persistent data unique to the machine */
431         r = sd_id128_get_machine((sd_id128_t*) v);
432         if (r < 0)
433                 return r;
434
435         /* combine with some data unique (on this machine) to this
436          * netdev */
437         memcpy(v + sizeof(sd_id128_t), ifname, l);
438
439         /* Let's hash the host machine ID plus the container name. We
440          * use a fixed, but originally randomly created hash key here. */
441         siphash24(result, v, sz, HASH_KEY.bytes);
442
443         assert_cc(ETH_ALEN <= sizeof(result));
444         memcpy(mac->ether_addr_octet, result, ETH_ALEN);
445
446         /* see eth_random_addr in the kernel */
447         mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
448         mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
449
450         *ret = mac;
451         mac = NULL;
452
453         return 0;
454 }
455
456 static int netdev_create(NetDev *netdev, Link *link,
457                          sd_rtnl_message_handler_t callback) {
458         int r;
459
460         assert(netdev);
461         assert(!link || callback);
462
463         /* create netdev */
464         if (NETDEV_VTABLE(netdev)->create) {
465                 assert(!link);
466
467                 r = NETDEV_VTABLE(netdev)->create(netdev);
468                 if (r < 0)
469                         return r;
470
471                 log_netdev_debug(netdev, "created");
472         } else {
473                 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
474
475                 r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0);
476                 if (r < 0) {
477                         log_netdev_error(netdev,
478                                          "Could not allocate RTM_NEWLINK message: %s",
479                                          strerror(-r));
480                         return r;
481                 }
482
483                 r = sd_rtnl_message_append_string(m, IFLA_IFNAME, netdev->ifname);
484                 if (r < 0) {
485                         log_netdev_error(netdev,
486                                          "Could not append IFLA_IFNAME, attribute: %s",
487                                          strerror(-r));
488                         return r;
489                 }
490
491                 if (netdev->mac) {
492                         r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac);
493                         if (r < 0) {
494                                 log_netdev_error(netdev,
495                                                  "Could not append IFLA_ADDRESS attribute: %s",
496                                                  strerror(-r));
497                             return r;
498                         }
499                 }
500
501                 if (netdev->mtu) {
502                         r = sd_rtnl_message_append_u32(m, IFLA_MTU, netdev->mtu);
503                         if (r < 0) {
504                                 log_netdev_error(netdev,
505                                                  "Could not append IFLA_MTU attribute: %s",
506                                                  strerror(-r));
507                                 return r;
508                         }
509                 }
510
511                 if (link) {
512                         r = sd_rtnl_message_append_u32(m, IFLA_LINK, link->ifindex);
513                         if (r < 0) {
514                                 log_netdev_error(netdev,
515                                                  "Could not append IFLA_LINK attribute: %s",
516                                                  strerror(-r));
517                                 return r;
518                         }
519                 }
520
521                 r = sd_rtnl_message_open_container(m, IFLA_LINKINFO);
522                 if (r < 0) {
523                         log_netdev_error(netdev,
524                                          "Could not append IFLA_LINKINFO attribute: %s",
525                                          strerror(-r));
526                         return r;
527                 }
528
529                 r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA,
530                                                          netdev_kind_to_string(netdev->kind));
531                 if (r < 0) {
532                         log_netdev_error(netdev,
533                                          "Could not append IFLA_INFO_DATA attribute: %s",
534                                          strerror(-r));
535                         return r;
536                 }
537
538                 if (NETDEV_VTABLE(netdev)->fill_message_create) {
539                         r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m);
540                         if (r < 0)
541                                 return r;
542                 }
543
544                 r = sd_rtnl_message_close_container(m);
545                 if (r < 0) {
546                         log_netdev_error(netdev,
547                                          "Could not append IFLA_LINKINFO attribute: %s",
548                                          strerror(-r));
549                         return r;
550                 }
551
552                 r = sd_rtnl_message_close_container(m);
553                 if (r < 0) {
554                         log_netdev_error(netdev,
555                                          "Could not append IFLA_LINKINFO attribute: %s",
556                                          strerror(-r));
557                         return r;
558                 }
559
560
561                 if (link) {
562                         r = sd_rtnl_call_async(netdev->manager->rtnl, m,
563                                                callback, link, 0, NULL);
564                         if (r < 0) {
565                                 log_netdev_error(netdev,
566                                                  "Could not send rtnetlink message: %s",
567                                                  strerror(-r));
568                                 return r;
569                         }
570
571                         link_ref(link);
572                 } else {
573                         r = sd_rtnl_call_async(netdev->manager->rtnl, m,
574                                                netdev_create_handler, netdev, 0,
575                                                NULL);
576                         if (r < 0) {
577                                 log_netdev_error(netdev,
578                                                  "Could not send rtnetlink message: %s",
579                                                  strerror(-r));
580                                 return r;
581                         }
582
583                         netdev_ref(netdev);
584                 }
585
586                 netdev->state = NETDEV_STATE_CREATING;
587
588                 log_netdev_debug(netdev, "creating");
589         }
590
591         return 0;
592 }
593
594 /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */
595 int netdev_join(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
596         int r;
597
598         assert(netdev);
599         assert(netdev->manager);
600         assert(netdev->manager->rtnl);
601         assert(NETDEV_VTABLE(netdev));
602
603         switch (NETDEV_VTABLE(netdev)->create_type) {
604         case NETDEV_CREATE_MASTER:
605                 r = netdev_enslave(netdev, link, callback);
606                 if (r < 0)
607                         return r;
608
609                 break;
610         case NETDEV_CREATE_STACKED:
611                 r = netdev_create(netdev, link, callback);
612                 if (r < 0)
613                         return r;
614
615                 break;
616         default:
617                 assert_not_reached("Can not join independent netdev");
618         }
619
620         return 0;
621 }
622
623 static int netdev_load_one(Manager *manager, const char *filename) {
624         _cleanup_netdev_unref_ NetDev *netdev = NULL;
625         _cleanup_free_ NetDev *netdev_raw = NULL;
626         _cleanup_fclose_ FILE *file = NULL;
627         int r;
628
629         assert(manager);
630         assert(filename);
631
632         file = fopen(filename, "re");
633         if (!file) {
634                 if (errno == ENOENT)
635                         return 0;
636                 else
637                         return -errno;
638         }
639
640         if (null_or_empty_fd(fileno(file))) {
641                 log_debug("Skipping empty file: %s", filename);
642                 return 0;
643         }
644
645         netdev_raw = new0(NetDev, 1);
646         if (!netdev_raw)
647                 return log_oom();
648
649         netdev_raw->kind = _NETDEV_KIND_INVALID;
650
651         r = config_parse(NULL, filename, file,
652                          "Match\0NetDev\0",
653                          config_item_perf_lookup, network_netdev_gperf_lookup,
654                          true, false, true, netdev_raw);
655         if (r < 0)
656                 return r;
657
658         r = fseek(file, 0, SEEK_SET);
659         if (r < 0)
660                 return -errno;
661
662         /* skip out early if configuration does not match the environment */
663         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
664                              netdev_raw->match_host, netdev_raw->match_virt,
665                              netdev_raw->match_kernel, netdev_raw->match_arch,
666                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
667                 return 0;
668
669         if (!NETDEV_VTABLE(netdev_raw)) {
670                 log_warning("NetDev with invalid Kind configured in %s. Ignoring", filename);
671                 return 0;
672         }
673
674         if (!netdev_raw->ifname) {
675                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
676                 return 0;
677         }
678
679         netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size);
680         if (!netdev)
681                 return log_oom();
682
683         netdev->n_ref = 1;
684         netdev->manager = manager;
685         netdev->state = _NETDEV_STATE_INVALID;
686         netdev->kind = netdev_raw->kind;
687         netdev->ifname = netdev_raw->ifname;
688
689         if (NETDEV_VTABLE(netdev)->init)
690                 NETDEV_VTABLE(netdev)->init(netdev);
691
692         r = config_parse(NULL, filename, file,
693                          NETDEV_VTABLE(netdev)->sections,
694                          config_item_perf_lookup, network_netdev_gperf_lookup,
695                          false, false, false, netdev);
696         if (r < 0)
697                 return r;
698
699         /* verify configuration */
700         if (NETDEV_VTABLE(netdev)->config_verify) {
701                 r = NETDEV_VTABLE(netdev)->config_verify(netdev, filename);
702                 if (r < 0)
703                         return 0;
704         }
705
706         netdev->filename = strdup(filename);
707         if (!netdev->filename)
708                 return log_oom();
709
710         if (!netdev->mac) {
711                 r = netdev_get_mac(netdev->ifname, &netdev->mac);
712                 if (r < 0) {
713                         log_error("Failed to generate predictable MAC address for %s",
714                                   netdev->ifname);
715                         return r;
716                 }
717         }
718
719         r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev);
720         if (r < 0)
721                 return r;
722
723         LIST_HEAD_INIT(netdev->callbacks);
724
725         log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
726
727         switch (NETDEV_VTABLE(netdev)->create_type) {
728         case NETDEV_CREATE_MASTER:
729         case NETDEV_CREATE_INDEPENDENT:
730                 r = netdev_create(netdev, NULL, NULL);
731                 if (r < 0)
732                         return 0;
733
734                 break;
735         default:
736                 break;
737         }
738
739         netdev = NULL;
740
741         return 0;
742 }
743
744 int netdev_load(Manager *manager) {
745         NetDev *netdev;
746         char **files, **f;
747         int r;
748
749         assert(manager);
750
751         while ((netdev = hashmap_first(manager->netdevs)))
752                 netdev_unref(netdev);
753
754         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
755         if (r < 0)
756                 return log_error_errno(r, "Failed to enumerate netdev files: %m");
757
758         STRV_FOREACH_BACKWARDS(f, files) {
759                 r = netdev_load_one(manager, *f);
760                 if (r < 0)
761                         return r;
762         }
763
764         strv_free(files);
765
766         return 0;
767 }