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