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