chiark / gitweb /
5ee257f984b815a97e3a2a4896acccf8fc200e13
[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 before 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         int r;
312
313         if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
314                 return netdev_create(netdev, link, callback);
315
316         if (netdev->state == NETDEV_STATE_READY) {
317                 r = netdev_enslave_ready(netdev, link, callback);
318                 if (r < 0)
319                         return r;
320         } else {
321                 /* the netdev is not yet read, save this request for when it is*/
322                 netdev_enslave_callback *cb;
323
324                 cb = new0(netdev_enslave_callback, 1);
325                 if (!cb)
326                         return log_oom();
327
328                 cb->callback = callback;
329                 cb->link = link;
330
331                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
332         }
333
334         return 0;
335 }
336
337 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
338         uint16_t type;
339         const char *kind;
340         char *received_kind;
341         char *received_name;
342         int r, ifindex;
343
344         assert(netdev);
345         assert(message);
346
347         r = sd_rtnl_message_get_type(message, &type);
348         if (r < 0) {
349                 log_error_netdev(netdev, "Could not get rtnl message type");
350                 return r;
351         }
352
353         if (type != RTM_NEWLINK) {
354                 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
355                 return -EINVAL;
356         }
357
358         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
359         if (r < 0) {
360                 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
361                 netdev_enter_failed(netdev);
362                 return r;
363         } else if (ifindex <= 0) {
364                 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
365                 netdev_enter_failed(netdev);
366                 return r;
367         }
368
369
370         if (netdev->ifindex > 0) {
371                 if (netdev->ifindex != ifindex) {
372                         log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
373                                          ifindex, netdev->ifindex);
374                         netdev_enter_failed(netdev);
375                         return -EEXIST;
376                 } else
377                         /* ifindex already set to the same for this netdev */
378                         return 0;
379         }
380
381         r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
382         if (r < 0) {
383                 log_error_netdev(netdev, "Could not get IFNAME");
384                 return r;
385         }
386
387         if (!streq(netdev->name, received_name)) {
388                 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
389                                  received_name);
390                 netdev_enter_failed(netdev);
391                 return r;
392         }
393
394         r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
395         if (r < 0) {
396                 log_error_netdev(netdev, "Could not get LINKINFO");
397                 return r;
398         }
399
400         r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
401         if (r < 0) {
402                 log_error_netdev(netdev, "Could not get KIND");
403                 return r;
404         }
405
406         r = sd_rtnl_message_exit_container(message);
407         if (r < 0) {
408                 log_error_netdev(netdev, "Could not exit container");
409                 return r;
410         }
411
412         kind = netdev_kind_to_string(netdev->kind);
413         if (!kind) {
414                 log_error_netdev(netdev, "Could not get kind");
415                 netdev_enter_failed(netdev);
416                 return -EINVAL;
417         }
418
419         if (!streq(kind, received_kind)) {
420                 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
421                                  "expected %s", received_kind, kind);
422                 netdev_enter_failed(netdev);
423                 return r;
424         }
425
426         netdev->ifindex = ifindex;
427
428         netdev_enter_ready(netdev);
429
430         return 0;
431 }
432
433 static int netdev_load_one(Manager *manager, const char *filename) {
434         _cleanup_netdev_unref_ NetDev *netdev = NULL;
435         _cleanup_fclose_ FILE *file = NULL;
436         int r;
437
438         assert(manager);
439         assert(filename);
440
441         if (null_or_empty_path(filename)) {
442                 log_debug("skipping empty file: %s", filename);
443                 return 0;
444         }
445
446         file = fopen(filename, "re");
447         if (!file) {
448                 if (errno == ENOENT)
449                         return 0;
450                 else
451                         return -errno;
452         }
453
454         netdev = new0(NetDev, 1);
455         if (!netdev)
456                 return log_oom();
457
458         netdev->n_ref = 1;
459         netdev->manager = manager;
460         netdev->state = _NETDEV_STATE_INVALID;
461         netdev->kind = _NETDEV_KIND_INVALID;
462         netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
463         netdev->vlanid = VLANID_MAX + 1;
464
465         r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
466                          config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
467                          false, false, netdev);
468         if (r < 0) {
469                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
470                 return r;
471         }
472
473         if (netdev->kind == _NETDEV_KIND_INVALID) {
474                 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
475                 return 0;
476         }
477
478         if (!netdev->name) {
479                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
480                 return 0;
481         }
482
483         if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
484                 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
485                 return 0;
486         }
487
488         if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
489                 log_warning("VLAN Id configured for a %s in %s. Ignoring",
490                             netdev_kind_to_string(netdev->kind), filename);
491                 return 0;
492         }
493
494         if (netdev->kind != NETDEV_KIND_MACVLAN &&
495             netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
496                 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
497                             netdev_kind_to_string(netdev->kind), filename);
498                 return 0;
499         }
500
501         netdev->filename = strdup(filename);
502         if (!netdev->filename)
503                 return log_oom();
504
505         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
506                              netdev->match_host, netdev->match_virt,
507                              netdev->match_kernel, netdev->match_arch,
508                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
509                 return 0;
510
511         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
512         if (r < 0)
513                 return r;
514
515         LIST_HEAD_INIT(netdev->callbacks);
516
517         if (netdev->kind != NETDEV_KIND_VLAN &&
518             netdev->kind != NETDEV_KIND_MACVLAN) {
519                 r = netdev_create(netdev, NULL, NULL);
520                 if (r < 0)
521                         return r;
522         }
523
524         log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind));
525
526         netdev = NULL;
527
528         return 0;
529 }
530
531 int netdev_load(Manager *manager) {
532         NetDev *netdev;
533         char **files, **f;
534         int r;
535
536         assert(manager);
537
538         while ((netdev = hashmap_first(manager->netdevs)))
539                 netdev_unref(netdev);
540
541         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
542         if (r < 0) {
543                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
544                 return r;
545         }
546
547         STRV_FOREACH_BACKWARDS(f, files) {
548                 r = netdev_load_one(manager, *f);
549                 if (r < 0)
550                         return r;
551         }
552
553         strv_free(files);
554
555         return 0;
556 }