chiark / gitweb /
service: add support for reboot argument when triggered by StartLimitAction=
[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 #include "siphash24.h"
29
30 #define VLANID_MAX 4094
31
32 static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
33         [NETDEV_KIND_BRIDGE] = "bridge",
34         [NETDEV_KIND_BOND] = "bond",
35         [NETDEV_KIND_VLAN] = "vlan",
36         [NETDEV_KIND_MACVLAN] = "macvlan",
37 };
38
39 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
40 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
41
42 static const char* const macvlan_mode_table[_NETDEV_MACVLAN_MODE_MAX] = {
43         [NETDEV_MACVLAN_MODE_PRIVATE] = "private",
44         [NETDEV_MACVLAN_MODE_VEPA] = "vepa",
45         [NETDEV_MACVLAN_MODE_BRIDGE] = "bridge",
46         [NETDEV_MACVLAN_MODE_PASSTHRU] = "passthru",
47 };
48
49 DEFINE_STRING_TABLE_LOOKUP(macvlan_mode, MacVlanMode);
50 DEFINE_CONFIG_PARSE_ENUM(config_parse_macvlan_mode, macvlan_mode, MacVlanMode, "Failed to parse macvlan mode");
51
52 void netdev_free(NetDev *netdev) {
53         netdev_enslave_callback *callback;
54
55         if (!netdev)
56                 return;
57
58         while ((callback = netdev->callbacks)) {
59                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
60                 free(callback);
61         }
62
63         if (netdev->name)
64                 hashmap_remove(netdev->manager->netdevs, netdev->name);
65
66         free(netdev->filename);
67
68         free(netdev->description);
69         free(netdev->name);
70
71         condition_free_list(netdev->match_host);
72         condition_free_list(netdev->match_virt);
73         condition_free_list(netdev->match_kernel);
74         condition_free_list(netdev->match_arch);
75
76         free(netdev);
77 }
78
79 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
80         NetDev *netdev;
81
82         assert(manager);
83         assert(name);
84         assert(ret);
85
86         netdev = hashmap_get(manager->netdevs, name);
87         if (!netdev) {
88                 *ret = NULL;
89                 return -ENOENT;
90         }
91
92         *ret = netdev;
93
94         return 0;
95 }
96
97 static int netdev_enter_failed(NetDev *netdev) {
98         netdev->state = NETDEV_STATE_FAILED;
99
100         return 0;
101 }
102
103 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
104         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
105         int r;
106
107         assert(netdev);
108         assert(netdev->state == NETDEV_STATE_READY);
109         assert(netdev->manager);
110         assert(netdev->manager->rtnl);
111         assert(link);
112         assert(callback);
113
114         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req,
115                                      RTM_SETLINK, link->ifindex);
116         if (r < 0) {
117                 log_error_netdev(netdev,
118                                  "Could not allocate RTM_SETLINK message: %s",
119                                  strerror(-r));
120                 return r;
121         }
122
123         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
124         if (r < 0) {
125                 log_error_netdev(netdev,
126                                  "Could not append IFLA_MASTER attribute: %s",
127                                  strerror(-r));
128                 return r;
129         }
130
131         r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
132         if (r < 0) {
133                 log_error_netdev(netdev,
134                                  "Could not send rtnetlink message: %s",
135                                  strerror(-r));
136                 return r;
137         }
138
139         log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
140
141         return 0;
142 }
143
144 static int netdev_enter_ready(NetDev *netdev) {
145         netdev_enslave_callback *callback;
146
147         assert(netdev);
148         assert(netdev->name);
149
150         if (netdev->state != NETDEV_STATE_CREATING)
151                 return 0;
152
153         netdev->state = NETDEV_STATE_READY;
154
155         log_info_netdev(netdev, "netdev ready");
156
157         LIST_FOREACH(callbacks, callback, netdev->callbacks) {
158                 /* enslave the links that were attempted to be enslaved befor the
159                  * link was ready */
160                 netdev_enslave_ready(netdev, callback->link, callback->callback);
161         }
162
163         return 0;
164 }
165 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
166         NetDev *netdev = userdata;
167         int r;
168
169         assert(netdev->state != _NETDEV_STATE_INVALID);
170
171         r = sd_rtnl_message_get_errno(m);
172         if (r == -EEXIST)
173                 log_debug_netdev(netdev, "netdev exists, using existing");
174         else if (r < 0) {
175                 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
176                 netdev_enter_failed(netdev);
177
178                 return 1;
179         }
180
181         return 1;
182 }
183
184 #define HASH_KEY SD_ID128_MAKE(52,e1,45,bd,00,6f,29,96,21,c6,30,6d,83,71,04,48)
185
186 static int netdev_get_mac(NetDev *netdev, struct ether_addr *mac) {
187         uint8_t result[8];
188         size_t l, sz;
189         uint8_t *v;
190         int r;
191
192         assert(netdev);
193         assert(netdev->name);
194         assert(mac);
195
196         l = strlen(netdev->name);
197         sz = sizeof(sd_id128_t) + l;
198         v = alloca(sz);
199
200         /* fetch some persistent data unique to the machine */
201         r = sd_id128_get_machine((sd_id128_t*) v);
202         if (r < 0)
203                 return r;
204
205         /* combine with some data unique (on this machine) to this
206          * netdev */
207         memcpy(v + sizeof(sd_id128_t), netdev->name, l);
208
209         /* Let's hash the host machine ID plus the container name. We
210          * use a fixed, but originally randomly created hash key here. */
211         siphash24(result, v, sz, HASH_KEY.bytes);
212
213         assert_cc(ETH_ALEN <= sizeof(result));
214         memcpy(mac->ether_addr_octet, result, ETH_ALEN);
215
216         /* see eth_random_addr in the kernel */
217         mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
218         mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
219
220         return 0;
221 }
222
223 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
224         _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL;
225         struct ether_addr mac;
226         const char *kind;
227         int r;
228
229         assert(netdev);
230         assert(!(netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN) ||
231                (link && callback));
232         assert(netdev->name);
233         assert(netdev->manager);
234         assert(netdev->manager->rtnl);
235
236         r = netdev_get_mac(netdev, &mac);
237         if (r < 0) {
238                 log_error("Failed to generate predictable MAC address for %s", netdev->name);
239                 return r;
240         }
241
242         r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, 0);
243         if (r < 0) {
244                 log_error_netdev(netdev,
245                                  "Could not allocate RTM_NEWLINK message: %s",
246                                  strerror(-r));
247                 return r;
248         }
249
250         if (link) {
251                 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
252                 if (r < 0) {
253                         log_error_netdev(netdev,
254                                          "Could not append IFLA_LINK attribute: %s",
255                                          strerror(-r));
256                         return r;
257                 }
258         }
259
260         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
261         if (r < 0) {
262                 log_error_netdev(netdev,
263                                  "Could not append IFLA_IFNAME attribute: %s",
264                                  strerror(-r));
265                 return r;
266         }
267
268         r = sd_rtnl_message_append_ether_addr(req, IFLA_ADDRESS, &mac);
269         if (r < 0) {
270                 log_error_netdev(netdev,
271                                  "Colud not append IFLA_ADDRESS attribute: %s",
272                                  strerror(-r));
273                 return r;
274         }
275
276         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
277         if (r < 0) {
278                 log_error_netdev(netdev,
279                                  "Could not open IFLA_LINKINFO container: %s",
280                                  strerror(-r));
281                 return r;
282         }
283
284         kind = netdev_kind_to_string(netdev->kind);
285         if (!kind) {
286                 log_error_netdev(netdev, "Invalid kind");
287                 return -EINVAL;
288         }
289
290         r = sd_rtnl_message_open_container_union(req, IFLA_INFO_DATA, kind);
291         if (r < 0) {
292                 log_error_netdev(netdev,
293                                  "Could not open IFLA_INFO_DATA container: %s",
294                                   strerror(-r));
295                 return r;
296         }
297
298         if (netdev->vlanid <= VLANID_MAX) {
299                 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
300                 if (r < 0) {
301                         log_error_netdev(netdev,
302                                          "Could not append IFLA_VLAN_ID attribute: %s",
303                                          strerror(-r));
304                         return r;
305                 }
306         }
307
308         if (netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
309         r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, netdev->macvlan_mode);
310         if (r < 0) {
311                 log_error_netdev(netdev,
312                                  "Could not append IFLA_MACVLAN_MODE attribute: %s",
313                                  strerror(-r));
314                         return r;
315                 }
316         }
317
318         r = sd_rtnl_message_close_container(req);
319         if (r < 0) {
320                 log_error_netdev(netdev,
321                                  "Could not close IFLA_INFO_DATA container %s",
322                                  strerror(-r));
323                 return r;
324         }
325
326         r = sd_rtnl_message_close_container(req);
327         if (r < 0) {
328                 log_error_netdev(netdev,
329                                  "Could not close IFLA_LINKINFO container %s",
330                                  strerror(-r));
331                 return r;
332         }
333
334         if (link)
335                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
336         else
337                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
338         if (r < 0) {
339                 log_error_netdev(netdev,
340                                  "Could not send rtnetlink message: %s", strerror(-r));
341                 return r;
342         }
343
344         log_debug_netdev(netdev, "creating netdev");
345
346         netdev->state = NETDEV_STATE_CREATING;
347
348         return 0;
349 }
350
351 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
352         if (netdev->kind == NETDEV_KIND_VLAN || netdev->kind == NETDEV_KIND_MACVLAN)
353                 return netdev_create(netdev, link, callback);
354
355         if (netdev->state == NETDEV_STATE_READY) {
356                 netdev_enslave_ready(netdev, link, callback);
357         } else {
358                 /* the netdev is not yet read, save this request for when it is*/
359                 netdev_enslave_callback *cb;
360
361                 cb = new0(netdev_enslave_callback, 1);
362                 if (!cb)
363                         return log_oom();
364
365                 cb->callback = callback;
366                 cb->link = link;
367
368                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
369         }
370
371         return 0;
372 }
373
374 int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) {
375         uint16_t type;
376         const char *kind;
377         char *received_kind;
378         char *received_name;
379         int r, ifindex;
380
381         assert(netdev);
382         assert(message);
383
384         r = sd_rtnl_message_get_type(message, &type);
385         if (r < 0) {
386                 log_error_netdev(netdev, "Could not get rtnl message type");
387                 return r;
388         }
389
390         if (type != RTM_NEWLINK) {
391                 log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type");
392                 return -EINVAL;
393         }
394
395         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
396         if (r < 0) {
397                 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
398                 netdev_enter_failed(netdev);
399                 return r;
400         } else if (ifindex <= 0) {
401                 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
402                 netdev_enter_failed(netdev);
403                 return r;
404         }
405
406
407         if (netdev->ifindex > 0) {
408                 if (netdev->ifindex != ifindex) {
409                         log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
410                                          ifindex, netdev->ifindex);
411                         netdev_enter_failed(netdev);
412                         return -EEXIST;
413                 } else
414                         /* ifindex already set to the same for this netdev */
415                         return 0;
416         }
417
418         r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name);
419         if (r < 0) {
420                 log_error_netdev(netdev, "Could not get IFNAME");
421                 return r;
422         }
423
424         if (!streq(netdev->name, received_name)) {
425                 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
426                                  received_name);
427                 netdev_enter_failed(netdev);
428                 return r;
429         }
430
431         r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
432         if (r < 0) {
433                 log_error_netdev(netdev, "Could not get LINKINFO");
434                 return r;
435         }
436
437         r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
438         if (r < 0) {
439                 log_error_netdev(netdev, "Could not get KIND");
440                 return r;
441         }
442
443         r = sd_rtnl_message_exit_container(message);
444         if (r < 0) {
445                 log_error_netdev(netdev, "Could not exit container");
446                 return r;
447         }
448
449         kind = netdev_kind_to_string(netdev->kind);
450         if (!kind) {
451                 log_error_netdev(netdev, "Could not get kind");
452                 netdev_enter_failed(netdev);
453                 return -EINVAL;
454         }
455
456         if (!streq(kind, received_kind)) {
457                 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
458                                  "expected %s", received_kind, kind);
459                 netdev_enter_failed(netdev);
460                 return r;
461         }
462
463         netdev->ifindex = ifindex;
464
465         netdev_enter_ready(netdev);
466
467         return 0;
468 }
469
470 static int netdev_load_one(Manager *manager, const char *filename) {
471         _cleanup_netdev_free_ NetDev *netdev = NULL;
472         _cleanup_fclose_ FILE *file = NULL;
473         int r;
474
475         assert(manager);
476         assert(filename);
477
478         if (null_or_empty_path(filename)) {
479                 log_debug("skipping empty file: %s", filename);
480                 return 0;
481         }
482
483         file = fopen(filename, "re");
484         if (!file) {
485                 if (errno == ENOENT)
486                         return 0;
487                 else
488                         return -errno;
489         }
490
491         netdev = new0(NetDev, 1);
492         if (!netdev)
493                 return log_oom();
494
495         netdev->manager = manager;
496         netdev->state = _NETDEV_STATE_INVALID;
497         netdev->kind = _NETDEV_KIND_INVALID;
498         netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
499         netdev->vlanid = VLANID_MAX + 1;
500
501         r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
502                          config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
503                          false, false, netdev);
504         if (r < 0) {
505                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
506                 return r;
507         }
508
509         if (netdev->kind == _NETDEV_KIND_INVALID) {
510                 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
511                 return 0;
512         }
513
514         if (!netdev->name) {
515                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
516                 return 0;
517         }
518
519         if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
520                 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
521                 return 0;
522         }
523
524         if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
525                 log_warning("VLAN Id configured for a %s in %s. Ignoring",
526                             netdev_kind_to_string(netdev->kind), filename);
527                 return 0;
528         }
529
530         if (netdev->kind != NETDEV_KIND_MACVLAN &&
531             netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
532                 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
533                             netdev_kind_to_string(netdev->kind), filename);
534                 return 0;
535         }
536
537         netdev->filename = strdup(filename);
538         if (!netdev->filename)
539                 return log_oom();
540
541         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
542                              netdev->match_host, netdev->match_virt,
543                              netdev->match_kernel, netdev->match_arch,
544                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
545                 return 0;
546
547         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
548         if (r < 0)
549                 return r;
550
551         LIST_HEAD_INIT(netdev->callbacks);
552
553         if (netdev->kind != NETDEV_KIND_VLAN &&
554             netdev->kind != NETDEV_KIND_MACVLAN) {
555                 r = netdev_create(netdev, NULL, NULL);
556                 if (r < 0)
557                         return r;
558         }
559
560         netdev = NULL;
561
562         return 0;
563 }
564
565 int netdev_load(Manager *manager) {
566         NetDev *netdev;
567         char **files, **f;
568         int r;
569
570         assert(manager);
571
572         while ((netdev = hashmap_first(manager->netdevs)))
573                 netdev_free(netdev);
574
575         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
576         if (r < 0) {
577                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
578                 return r;
579         }
580
581         STRV_FOREACH_BACKWARDS(f, files) {
582                 r = netdev_load_one(manager, *f);
583                 if (r < 0)
584                         return r;
585         }
586
587         strv_free(files);
588
589         return 0;
590 }