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