chiark / gitweb /
network: move configuration to /etc/systemd/network
[elogind.git] / src / udev / net / link-config.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4  This file is part of systemd.
5
6  Copyright (C) 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 <netinet/ether.h>
23 #include <net/if.h>
24
25 #include "sd-id128.h"
26
27 #include "link-config.h"
28 #include "ethtool-util.h"
29
30 #include "libudev-private.h"
31 #include "sd-rtnl.h"
32 #include "util.h"
33 #include "log.h"
34 #include "strv.h"
35 #include "path-util.h"
36 #include "conf-parser.h"
37 #include "conf-files.h"
38 #include "fileio.h"
39 #include "hashmap.h"
40
41 struct link_config_ctx {
42         LIST_HEAD(link_config, links);
43
44         int ethtool_fd;
45
46         sd_rtnl *rtnl;
47
48         char **link_dirs;
49         usec_t *link_dirs_ts_usec;
50 };
51
52 int link_config_ctx_new(link_config_ctx **ret) {
53         link_config_ctx *ctx;
54         int r;
55
56         if (!ret)
57                 return -EINVAL;
58
59         ctx = new0(link_config_ctx, 1);
60         if (!ctx)
61                 return -ENOMEM;
62
63         r = ethtool_connect(&ctx->ethtool_fd);
64         if (r < 0) {
65                 link_config_ctx_free(ctx);
66                 return r;
67         }
68
69         r = sd_rtnl_open(0, &ctx->rtnl);
70         if (r < 0) {
71                 link_config_ctx_free(ctx);
72                 return r;
73         }
74
75         LIST_HEAD_INIT(ctx->links);
76
77         ctx->link_dirs = strv_new("/etc/systemd/network",
78                                   "/run/systemd/network",
79                                   "/usr/lib/systemd/network",
80                                   NULL);
81         if (!ctx->link_dirs) {
82                 log_error("failed to build link config directory array");
83                 link_config_ctx_free(ctx);
84                 return -ENOMEM;
85         }
86         if (!path_strv_canonicalize_uniq(ctx->link_dirs)) {
87                 log_error("failed to canonicalize link config directories\n");
88                 link_config_ctx_free(ctx);
89                 return -ENOMEM;
90         }
91
92         ctx->link_dirs_ts_usec = calloc(strv_length(ctx->link_dirs), sizeof(usec_t));
93         if(!ctx->link_dirs_ts_usec) {
94                 link_config_ctx_free(ctx);
95                 return -ENOMEM;
96         }
97
98         *ret = ctx;
99         return 0;
100 }
101
102 static void link_configs_free(link_config_ctx *ctx) {
103         link_config *link, *link_next;
104
105         if (!ctx)
106                 return;
107
108         LIST_FOREACH_SAFE(links, link, link_next, ctx->links) {
109                 free(link->filename);
110                 free(link->match_path);
111                 free(link->match_driver);
112                 free(link->match_type);
113                 free(link->description);
114
115                 free(link);
116         }
117 }
118
119 void link_config_ctx_free(link_config_ctx *ctx) {
120         if (!ctx)
121                 return;
122
123         if (ctx->ethtool_fd >= 0)
124                 close_nointr_nofail(ctx->ethtool_fd);
125
126         sd_rtnl_unref(ctx->rtnl);
127
128         strv_free(ctx->link_dirs);
129         free(ctx->link_dirs_ts_usec);
130         link_configs_free(ctx);
131
132         free(ctx);
133
134         return;
135 }
136
137 static int load_link(link_config_ctx *ctx, const char *filename) {
138         link_config *link;
139         FILE *file;
140         int r;
141
142         file = fopen(filename, "re");
143         if (!file) {
144                 if (errno == ENOENT)
145                         return 0;
146                 else
147                         return errno;
148         }
149
150         link = new0(link_config, 1);
151         if (!link) {
152                 r = log_oom();
153                 goto failure;
154         }
155
156         r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", config_item_perf_lookup,
157                          (void*) link_config_gperf_lookup, false, false, link);
158         if (r < 0) {
159                 log_warning("Colud not parse config file %s: %s", filename, strerror(-r));
160                 goto failure;
161         } else
162                 log_info("Parsed configuration file %s", filename);
163
164         link->filename = strdup(filename);
165
166         LIST_PREPEND(links, ctx->links, link);
167
168         return 0;
169
170 failure:
171         free(link);
172         return r;
173 }
174
175 int link_config_load(link_config_ctx *ctx) {
176         int r;
177         char **files, **f;
178
179         link_configs_free(ctx);
180
181         /* update timestamps */
182         paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, true);
183
184         r = conf_files_list_strv(&files, ".link", NULL, (const char **)ctx->link_dirs);
185         if (r < 0) {
186                 log_error("failed to enumerate link files: %s", strerror(-r));
187                 return r;
188         }
189
190         STRV_FOREACH_BACKWARDS(f, files) {
191                 r = load_link(ctx, *f);
192                 if (r < 0)
193                         return r;
194         }
195
196         return 0;
197 }
198
199 bool link_config_should_reload(link_config_ctx *ctx) {
200         return paths_check_timestamp(ctx->link_dirs, ctx->link_dirs_ts_usec, false);
201 }
202
203 static bool match_config(link_config *match, struct udev_device *device) {
204         const char *property;
205
206         if (match->match_mac) {
207                 property = udev_device_get_sysattr_value(device, "address");
208                 if (!property || !streq(match->match_mac, property)) {
209                         log_debug("Device MAC address (%s) did not match MACAddress=%s", property, match->match_mac);
210                         return 0;
211                 }
212         }
213
214         if (match->match_path) {
215                 property = udev_device_get_property_value(device, "ID_PATH");
216                 if (!property || !streq(match->match_path, property)) {
217                         log_debug("Device's persistent path (%s) did not match Path=%s", property, match->match_path);
218                         return 0;
219                 }
220         }
221
222         if (match->match_driver) {
223                 property = udev_device_get_driver(device);
224                 if (!property || !streq(match->match_driver, property)) {
225                         log_debug("Device driver (%s) did not match Driver=%s", property, match->match_driver);
226                         return 0;
227                 }
228         }
229
230         if (match->match_type) {
231                 property = udev_device_get_devtype(device);
232                 if (!property || !streq(match->match_type, property)) {
233                         log_debug("Device type (%s) did not match Type=%s", property, match->match_type);
234                         return 0;
235                 }
236         }
237
238         return 1;
239 }
240
241 int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config **ret) {
242         link_config *link;
243
244         LIST_FOREACH(links, link, ctx->links) {
245                 if (!match_config(link, device)) {
246                         log_info("Config file %s does not apply to device %s", link->filename, udev_device_get_sysname(device));
247                 } else {
248                         log_info("Config file %s applies to device %s", link->filename, udev_device_get_sysname(device));
249                         *ret = link;
250                         return 0;
251                 }
252         }
253
254         return -ENOENT;
255 }
256
257 static int rtnl_set_properties(sd_rtnl *rtnl, int ifindex, const char *name, const struct ether_addr *mac, unsigned int mtu) {
258         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *message;
259         bool need_update = false;
260         int r;
261
262         assert(rtnl);
263         assert(ifindex > 0);
264
265         r = sd_rtnl_message_link_new(RTM_NEWLINK, ifindex, 0, 0, &message);
266         if (r < 0)
267                 return r;
268
269         if (name) {
270                 r = sd_rtnl_message_append(message, IFLA_IFNAME, name);
271                 if (r < 0)
272                         return r;
273
274                 need_update = true;
275         }
276
277         if (mac) {
278                 r = sd_rtnl_message_append(message, IFLA_ADDRESS, mac);
279                 if (r < 0)
280                         return r;
281
282                 need_update = true;
283         }
284
285         if (mtu > 0) {
286                 r = sd_rtnl_message_append(message, IFLA_MTU, &mtu);
287                 if (r < 0)
288                         return r;
289
290                 need_update = true;
291         }
292
293         if  (need_update) {
294                 r = sd_rtnl_send_with_reply_and_block(rtnl, message, 5 * USEC_PER_SEC, NULL);
295                 if (r < 0)
296                         return r;
297         }
298
299         return 0;
300 }
301
302 static bool enable_name_policy(void) {
303         _cleanup_free_ char *line;
304         char *w, *state;
305         int r;
306         size_t l;
307
308         r = read_one_line_file("/proc/cmdline", &line);
309         if (r < 0) {
310                 log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
311                 return true; /* something is very wrong, let's not make it worse */
312         }
313
314         FOREACH_WORD_QUOTED(w, l, line, state)
315                 if (strneq(w, "net.ifnames=0", l))
316                         return false;
317
318         return true;
319 }
320
321 static bool mac_is_random(struct udev_device *device) {
322         const char *s;
323         int type;
324
325         s = udev_device_get_sysattr_value(device, "addr_assign_type");
326         if (!s)
327                 return -EINVAL;
328         type = strtoul(s, NULL, 0);
329
330         /* check for NET_ADDR_RANDOM */
331         return type == 1;
332 }
333
334 static bool mac_is_permanent(struct udev_device *device) {
335         const char *s;
336         int type;
337
338         s = udev_device_get_sysattr_value(device, "addr_assign_type");
339         if (!s)
340                 return -EINVAL;
341         type = strtoul(s, NULL, 0);
342
343         /* check for NET_ADDR_PERM */
344         return type == 0;
345 }
346
347 static int get_mac(struct udev_device *device, bool want_random, struct ether_addr **ret) {
348         struct ether_addr *mac;
349         unsigned int seed;
350         int r, i;
351
352         mac = calloc(1, sizeof(struct ether_addr));
353         if (!mac)
354                 return -ENOMEM;
355
356         if (want_random)
357                 seed = random_u();
358         else {
359                 const char *name;
360                 sd_id128_t machine;
361                 char machineid_buf[33];
362                 const char *seed_str;
363
364                 /* fetch some persistent data unique (on this machine) to this device */
365                 name = udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD");
366                 if (!name) {
367                         name = udev_device_get_property_value(device, "ID_NET_NAME_SLOT");
368                         if (!name) {
369                                 name = udev_device_get_property_value(device, "ID_NET_NAME_PATH");
370                                 if (!name)
371                                         return -1;
372                         }
373                 }
374                 /* fetch some persistent data unique to this machine */
375                 r = sd_id128_get_machine(&machine);
376                 if (r < 0)
377                         return -1;
378
379                 /* combine the data */
380                 seed_str = strappenda(name, sd_id128_to_string(machine, machineid_buf));
381
382                 /* hash to get seed */
383                 seed = string_hash_func(seed_str);
384         }
385
386         srandom(seed);
387
388         for(i = 0; i < ETH_ALEN; i++) {
389                 mac->ether_addr_octet[i] = random();
390         }
391
392         /* see eth_random_addr in the kernel */
393         mac->ether_addr_octet[0] &= 0xfe;        /* clear multicast bit */
394         mac->ether_addr_octet[0] |= 0x02;        /* set local assignment bit (IEEE802) */
395
396         *ret = mac;
397
398         return 0;
399 }
400
401 int link_config_apply(link_config_ctx *ctx, link_config *config, struct udev_device *device) {
402         const char *name;
403         char *new_name = NULL;
404         struct ether_addr *mac = NULL;
405         int r, ifindex;
406
407         name = udev_device_get_sysname(device);
408         if (!name)
409                 return -EINVAL;
410
411         log_info("Configuring %s", name);
412
413         if (config->description) {
414                 r = udev_device_set_sysattr_value(device, "ifalias",
415                                                   config->description);
416                 if (r < 0)
417                         log_warning("Could not set description of %s to '%s': %s",
418                                     name, config->description, strerror(-r));
419                 else
420                         log_info("Set link description of %s to '%s'", name,
421                                  config->description);
422         }
423
424         if (config->speed || config->duplex) {
425                 r = ethtool_set_speed(ctx->ethtool_fd, name,
426                                       config->speed, config->duplex);
427                 if (r < 0)
428                         log_warning("Could not set speed or duplex of %s to %u Mbytes (%s): %s",
429                                     name, config->speed, config->duplex, strerror(-r));
430                 else
431                         log_info("Set speed or duplex of %s to %u Mbytes (%s)", name,
432                                  config->speed, config->duplex);
433         }
434
435         if (config->wol) {
436                 r = ethtool_set_wol(ctx->ethtool_fd, name, config->wol);
437                 if (r < 0)
438                         log_warning("Could not set WakeOnLan of %s to %s: %s",
439                                     name, config->wol, strerror(-r));
440                 else
441                         log_info("Set WakeOnLan of %s to %s", name, config->wol);
442         }
443
444         ifindex = udev_device_get_ifindex(device);
445         if (ifindex <= 0) {
446                 log_warning("Could not find ifindex");
447                 return -ENODEV;
448         }
449
450         if (config->name_policy && enable_name_policy()) {
451                 char **policy;
452
453                 STRV_FOREACH(policy, config->name_policy) {
454                         if (streq(*policy, "onboard")) {
455                                 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_ONBOARD"), &new_name);
456                                 if (r < 0)
457                                         return r;
458                                 if (new_name)
459                                         break;
460                         } else if (streq(*policy, "slot")) {
461                                 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_SLOT"), &new_name);
462                                 if (r < 0)
463                                         return r;
464                                 if (new_name)
465                                         break;
466                         } else if (streq(*policy, "path")) {
467                                 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_PATH"), &new_name);
468                                 if (r < 0)
469                                         return r;
470                                 if (new_name)
471                                         break;
472                         } else if (streq(*policy, "mac")) {
473                                 r = strdup_or_null(udev_device_get_property_value(device, "ID_NET_NAME_MAC"), &new_name);
474                                 if (r < 0)
475                                         return r;
476                                 if (new_name)
477                                         break;
478                         } else
479                                 log_warning("Invalid link naming policy '%s', ignoring.", *policy);
480                 }
481         }
482
483         if (!new_name && config->name) {
484                 new_name = calloc(1, IFNAMSIZ);
485                 strscpy(new_name, IFNAMSIZ, config->name);
486         }
487
488         if (config->mac_policy) {
489                 if (streq(config->mac_policy, "persistent")) {
490                         if (!mac_is_permanent(device)) {
491                                 r = get_mac(device, false, &mac);
492                                 if (r < 0)
493                                         return r;
494                         }
495                 } else if (streq(config->mac_policy, "random")) {
496                         if (!mac_is_random(device)) {
497                                 r = get_mac(device, true, &mac);
498                                 if (r < 0)
499                                         return r;
500                         }
501                 } else
502                         log_warning("Invalid MACAddress policy '%s', ignoring.", config->mac_policy);
503         }
504
505         if (!mac && config->mac) {
506                 mac = calloc(1, sizeof(struct ether_addr));
507                 r = sscanf(config->mac, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
508                            &mac->ether_addr_octet[0],
509                            &mac->ether_addr_octet[1],
510                            &mac->ether_addr_octet[2],
511                            &mac->ether_addr_octet[3],
512                            &mac->ether_addr_octet[4],
513                            &mac->ether_addr_octet[5]);
514                 if (r != 6) {
515                         r = -EINVAL;
516                         goto out;
517                 }
518         }
519
520         r = rtnl_set_properties(ctx->rtnl, ifindex, new_name, mac, config->mtu);
521         if (r < 0) {
522                 log_warning("Could not set Name, MACAddress or MTU on %s: %s", name, strerror(-r));
523                 goto out;
524         }
525
526         return 0;
527 out:
528         free(new_name);
529         free(mac);
530         return r;
531 }