chiark / gitweb /
30cb74b8b75fe5e1dc8fef6523950900c4bc9a17
[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_read_string(message, IFLA_IFNAME, &received_name);
396         if (r < 0) {
397                 log_error_netdev(netdev, "Could not get IFNAME");
398                 return r;
399         }
400
401         if (!streq(netdev->name, received_name)) {
402                 log_error_netdev(netdev, "Received newlink with wrong IFNAME %s",
403                                  received_name);
404                 netdev_enter_failed(netdev);
405                 return r;
406         }
407
408         r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO);
409         if (r < 0) {
410                 log_error_netdev(netdev, "Could not get LINKINFO");
411                 return r;
412         }
413
414         r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind);
415         if (r < 0) {
416                 log_error_netdev(netdev, "Could not get KIND");
417                 return r;
418         }
419
420         r = sd_rtnl_message_exit_container(message);
421         if (r < 0) {
422                 log_error_netdev(netdev, "Could not exit container");
423                 return r;
424         }
425
426         kind = netdev_kind_to_string(netdev->kind);
427         if (!kind) {
428                 log_error_netdev(netdev, "Could not get kind");
429                 netdev_enter_failed(netdev);
430                 return -EINVAL;
431         }
432
433         if (!streq(kind, received_kind)) {
434                 log_error_netdev(netdev, "Received newlink with wrong KIND %s, "
435                                  "expected %s", received_kind, kind);
436                 netdev_enter_failed(netdev);
437                 return r;
438         }
439
440         r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
441         if (r < 0) {
442                 log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r));
443                 netdev_enter_failed(netdev);
444                 return r;
445         } else if (ifindex <= 0) {
446                 log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex);
447                 netdev_enter_failed(netdev);
448                 return r;
449         }
450
451         if (netdev->ifindex > 0 && netdev->ifindex != ifindex) {
452                 log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d",
453                                  ifindex, netdev->ifindex);
454                 netdev_enter_failed(netdev);
455                 return -EEXIST;
456         }
457
458         netdev->ifindex = ifindex;
459
460         netdev_enter_ready(netdev);
461
462         return 0;
463 }
464
465 static int netdev_load_one(Manager *manager, const char *filename) {
466         _cleanup_netdev_free_ NetDev *netdev = NULL;
467         _cleanup_fclose_ FILE *file = NULL;
468         int r;
469
470         assert(manager);
471         assert(filename);
472
473         file = fopen(filename, "re");
474         if (!file) {
475                 if (errno == ENOENT)
476                         return 0;
477                 else
478                         return -errno;
479         }
480
481         netdev = new0(NetDev, 1);
482         if (!netdev)
483                 return log_oom();
484
485         netdev->manager = manager;
486         netdev->state = _NETDEV_STATE_INVALID;
487         netdev->kind = _NETDEV_KIND_INVALID;
488         netdev->macvlan_mode = _NETDEV_MACVLAN_MODE_INVALID;
489         netdev->vlanid = VLANID_MAX + 1;
490
491         r = config_parse(NULL, filename, file, "Match\0NetDev\0VLAN\0MACVLAN\0",
492                          config_item_perf_lookup, (void*) network_netdev_gperf_lookup,
493                          false, false, netdev);
494         if (r < 0) {
495                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
496                 return r;
497         }
498
499         if (netdev->kind == _NETDEV_KIND_INVALID) {
500                 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
501                 return 0;
502         }
503
504         if (!netdev->name) {
505                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
506                 return 0;
507         }
508
509         if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid > VLANID_MAX) {
510                 log_warning("VLAN without valid Id configured in %s. Ignoring", filename);
511                 return 0;
512         }
513
514         if (netdev->kind != NETDEV_KIND_VLAN && netdev->vlanid <= VLANID_MAX) {
515                 log_warning("VLAN Id configured for a %s in %s. Ignoring",
516                             netdev_kind_to_string(netdev->kind), filename);
517                 return 0;
518         }
519
520         if (netdev->kind != NETDEV_KIND_MACVLAN &&
521             netdev->macvlan_mode != _NETDEV_MACVLAN_MODE_INVALID) {
522                 log_warning("MACVLAN Mode configured for a %s in %s. Ignoring",
523                             netdev_kind_to_string(netdev->kind), filename);
524                 return 0;
525         }
526
527         netdev->filename = strdup(filename);
528         if (!netdev->filename)
529                 return log_oom();
530
531         if (net_match_config(NULL, NULL, NULL, NULL, NULL,
532                              netdev->match_host, netdev->match_virt,
533                              netdev->match_kernel, netdev->match_arch,
534                              NULL, NULL, NULL, NULL, NULL, NULL) <= 0)
535                 return 0;
536
537         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
538         if (r < 0)
539                 return r;
540
541         LIST_HEAD_INIT(netdev->callbacks);
542
543         if (netdev->kind != NETDEV_KIND_VLAN &&
544             netdev->kind != NETDEV_KIND_MACVLAN) {
545                 r = netdev_create(netdev, NULL, NULL);
546                 if (r < 0)
547                         return r;
548         }
549
550         netdev = NULL;
551
552         return 0;
553 }
554
555 int netdev_load(Manager *manager) {
556         NetDev *netdev;
557         char **files, **f;
558         int r;
559
560         assert(manager);
561
562         while ((netdev = hashmap_first(manager->netdevs)))
563                 netdev_free(netdev);
564
565         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
566         if (r < 0) {
567                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
568                 return r;
569         }
570
571         STRV_FOREACH_BACKWARDS(f, files) {
572                 r = netdev_load_one(manager, *f);
573                 if (r < 0)
574                         return r;
575         }
576
577         strv_free(files);
578
579         return 0;
580 }