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