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